#! /usr/bin/perl

#################################
#
# revision history 
# 
# Nicky - - 20100813 - init draft for WATS2.5, base on previous cli.pm optimize reliability, performance and add
#                   ssh, shell operation methods.
#                   The reason of do not update base on cli.pm is because there is already lot of case use cli.pm,
#                   and this new class need to have some interface change. 
# Nicky - 20100820 - update cmd method to support output informaiton 
# Sun Yan - 20100902 - add ping method; 
# Nicky - 20100907 - add reset_factory_default method
# Nikcy - 20100909 - update connect_cli connect_shell to resolve potential frequent connect/disconnect risk
# Nicky - 20100914 - update connect_cli and connect_shell, solve the problem of ssh identification change problem
# Nicky -20101102 - update connect_cli to make it force kick out admin user
# Nicky - 20101103 - add sleep interval according globle config in method cmd
# Nicky - 20101105 - add one line in cmd method to judge if(defined($successfully_matching_string))
# Nicky - 20110124 - add obtain_systeminfo, obtain_serial method for CLI
# Michael - 20110128- add configBovpn function
# Michael - 20110214- add Muvpn functions
# Michael - 20110216- expire old bovpn api
# Michael - 20110225- add multicast support of configBovpn
# Lizhi - 20110725-modify ConfigBpvpn_DD, 11.5 behavior change,if version >= 11.5,need yes when config domain_name
# Lizhi - 20110727-modify configBovpn,just like ConfigBovpn_DD
# Lizhi - 20110802-modfit cmd, expect "Default gateway:"
# Lizhi - 20110804-modify "Diffe-Hellman" to "Diffie-Hellman"
# Shewn - 20120104- modify "config_sslvpn" to support more type sslvpn and parameters
# Eric - 20120914 - update ping to support IPv6 address
#################################

package XtmCli;

#use Data::Dumper;
use Expect;
use ConfigurationManager;
my $config = new ConfigurationManager;
my $tmp_dir = $config->getTempDirectory;
my $error_tips = qr/(\^|Error|Failed|unexpected)/;
# /*! 
#     @header XtmCli 
#     <h2>Description</h2>
#     <pre>    XTM CLI opeation class.
#           </pre> 
# */



# /*!
#    @group  Constructor
# */

# /*!
#    @function new  
#        @param HASH Any of the following key-value pair in the HASH are OPTIONAL when you new the object, provide key-pair only when you need use them.
#        @param CLIMode=>'climode', use cli via 'console'(serial port) or 'ssh'. default set as 'ssh' mode.
#        @param HostIP=>'x.x.x.x', xtm box eth1 IP address
#        @param SSHPort=>'sshport', ssh connection port, default set as 4118
#        @param SNIP=>'x.x.x.x', box connected SN device's IP address.
#        @param SNPort=>'snport', box connected SN device's corresponding port num.
#        @param AdminUser=>'adminuser', xtm admin user name, default set as 'admin'
#        @param AdminPasswd=>'adminpasswd', xtm admin user's password, default set as 'readwrite'
#        @param RootUser=>'rootuser', xtm root user name, default set as 'root'
#        @param RootPasswd=>'rootpasswd', xtm root user's password, default set as 'readwrite'
#   
#    @return  
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>   Create a XtmCli Object. </pre>
#    <h2>Example:</h2>
#    <pre>my $cli= new XtmCli;
#                   my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');               
#                   #...
#       </pre>
#
# */
sub new{
    my $class = shift;
    my %params = @_;
        
        #set default value of attribute, these attributes are allowed to transmit from param
    my $self = {
            'CLIMode' =>'ssh',
            'HostIP' =>undef,
            'SSHPort'=>4118,
            'SNIP'=>undef,
            'SNPort'=>undef,
            'AdminUser'=>'admin',
            'AdminPasswd'=>'readwrite',
            'RootUser'=>'root',
            'RootPasswd'=>'readwrite',
            'Model'=>undef,
        };
    bless $self,$class;
    
    for my $attrib ( keys %params ) {

        die "Invalid parameter '$attrib' passed to '$class' constructor"
        unless exists $self->{$attrib};

        $self->{$attrib}= $params{$attrib} ;
    }

        $self->{Handle} = undef;
        $self->{LatestPrompt}=undef;
        
        #following attribute is internal private use only. 
        #{IfNewHandle}It is use for judge if need call disconnect at the end of some method which use cli do something
        #{HandleType} is to record what kind of handle process it was created. console/ssh
        $self->{IfNewHandle}= 1;
        $self->{HandleType}=undef;
        $self->{ConnectNum}=0;
        
    return $self;
};

#Destroy Method

# Nicky :( - seems do disconnect in DESTROY does not work, the handle becomes undef too early. it maybe caused by the charactor of the Expect.
#sub DESTROY{
#   
#   my $self=shift;
#   print "XtmCli:DESTROY - Do disconnect\n";   
#   print "self=$self";
#   print "Handle=",$self->handle," climode=",$self->cli_mode,"\n";
#   print "snport=",$self->sn_port,"\n";
#   $self->disconnect;
#   
#};
# /*!
#    @group Attribute operation 
# */

# /*!
#    @function cli_mode
#    @param [optional] $CLIMode If not provide, the sub will get the 'CLIMode'. Otherwise, 'CLIMode' will be set as input. The valid input is 'ssh' or 'console'
#    @return current cli mode (ssh or console)
#    @discussion 
#    <h2>Description:</h2> 
#    <pre> Get or set CLI mode. Ssh mode means CLI will be lanuched via ssh. Console mode means CLI will be launched via serial port.    </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  </pre>
#        <pre> my $cliMode=$cli1->cli_mode;
#        <pre> or my $cliMode=$cli1->cli_mode('console');   </pre>
#
# */

sub cli_mode {
    my $self = shift;
    if (@_) { 
        if(($_[0] eq 'ssh')||($_[0] eq 'console')){
            $self->{CLIMode} = shift ;
        }
        else{
            print "invalid input value $_[0] for XtmCli->climode. Should input ssh or console \n";
            }
    }
    return $self->{CLIMode};
    }
    

# /*!
#    @function mode
#    @param [optional] $Mode If not provide, the sub will get the 'CLIMode'. Otherwise, 'CLIMode' will be set as input. The valid input is 'ssh' or 'console'
#    @return current cli mode (ssh or console)
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> This method is exact same with method 'cli_mode', just provide friendly terminology. Get or set CLI mode. 
#    <pre> Ssh mode means CLI will be lanuched via ssh. Console mode means CLI will be launched via serial port   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $cliMode=$cli1->mode; 
#        <pre> my $cliMode=$cli1->mode('console'); </pre>
#
# */

sub mode {
    my $self = shift;
    if (@_) { 
        if(($_[0] eq 'ssh')||($_[0] eq 'console')){
            $self->{CLIMode} = shift ;
        }
        else{
            print "invalid input value $_[0] for XtmCli->climode. Should input ssh or console \n";
            }
    }
    return $self->{CLIMode};
    }

# /*!
#    @function host_ip
#    @param [optional] $hostip If not provide, the sub will get the 'HostIP'. Otherwise, 'HostIP' will be set as input. 
#    @return current hostip
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Get or set hostip which is used for access CLI via ssh.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $HostIP=$cli1->host_ip;
#        <pre> my $HostIP=$cli1->host_ip('x.x.x.x');    </pre>
#
# */
sub host_ip{
      my $self = shift;
    if (@_) { $self->{HostIP} = shift }
    return $self->{HostIP};
    }
    

# /*!
#    @function ssh_port
#    @param [optional] $sshport If not provide, the sub will get the 'SSHPort'. Otherwise, 'SSHPort' will be set as input. 
#    @return current SSHPort
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Get or set SSHPort which use for access CLI via ssh.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $SSHPort=$cli1->ssh_port; 
#        <pre> my $SSHPort=$cli1->ssh_port('888');</pre>
#
# */
sub ssh_port{
      my $self = shift;
    if (@_) { $self->{SSHPort} = shift }
    return $self->{SSHPort};
    }


# /*!
#    @function sn_ip
#    @param [optional] $snip If not provide, the sub will get the 'SNIP'. Otherwise, 'SNIP' will be set as input. 
#    @return current SNIP
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Get or set SNIP which use for access CLI via serial port.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $SNIP=$cli1->sn_ip;
#        <pre> my $SNIP=$cli1->sn_ip('x.x.x.x');    </pre>
#
# */
sub sn_ip{
      my $self = shift;
    if (@_) { $self->{SNIP} = shift }
    return $self->{SNIP};
    }
    

# /*!
#    @function sn_port
#    @param [optional] $snport If not provide, the sub will get the 'SNPort'. Otherwise, 'SNPort' will be set as input. 
#    @return current SNPort
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Get or set SNPort which use for access CLI via serial port.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $SNPort=$cli1->sn_port;   
#        <pre> my $SNPort=$cli1->sn_port('5003');   </pre>
#
# */
sub sn_port{
      my $self = shift;
    if (@_) { $self->{SNPort} = shift }
    return $self->{SNPort};
    }   



# /*!
#    @function admin_user
#    @param [optional] $adminuser, If not provide, the sub will get the 'AdminUser'. Otherwise, 'AdminUser' will be set as input. 
#    @return current SNPort
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>  Get or set AdminUser which is used for access CLI.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $AdminUser=$cli1->admin_user;
#        <pre> my $AdminUser=$cli1->admin_user('administrator');    </pre>
#
# */
sub admin_user{
      my $self = shift;
    if (@_) { $self->{AdminUser} = shift }
    return $self->{AdminUser};
    }   
    
    
# /*!
#    @function admin_passwd
#    @param [optional] $adminpasswd string. If not provide, the sub will get the 'AdminPasswd'. Otherwise, 'AdminPasswd' will be set as input. 
#    @return current AdminPasswd
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>   Get or set AdminPasswd which is used for access CLI.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $AdminPassword=$cli1->admin_passwd;
#        <pre> my $AdminPassword=$cli1->admin_passwd('password');   </pre>
#
# */
sub admin_passwd{
      my $self = shift;
    if (@_) { $self->{AdminPasswd} = shift }
    return $self->{AdminPasswd};
    }   
    
    
# /*!
#    @function root_user
#    @param [optional] $rootuser If not provide, the sub will get the 'RootUser'. Otherwise, 'RootUser' will be set as input. 
#    @return current RootUser
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Get or set RootUser which is used for access WG Linux shell.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $RootUser=$cli1->root_user;
#        <pre> my $RootUser=$cli1->root_user('rootuser');   </pre>
#
# */
sub root_user{
      my $self = shift;
    if (@_) { $self->{RootUser} = shift }
    return $self->{RootUser};
    }   


# /*!
#    @function root_passwd
#    @param [optional] $rootpasswd string. If not provide, the sub will get the 'RootPasswd'. Otherwise, 'RootPasswd' will be set as input. 
#    @return current RootPasswd
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>  Get or set RootPasswd which is used for access WG Linux shell.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $RootPassword=$cli1->root_passwd; 
#        <pre> my $RootPassword=$cli1->root_passwd('password');</pre>
#
# */
sub root_passwd{
      my $self = shift;
    if (@_) { $self->{RootPasswd} = shift }
    return $self->{RootPasswd};
    }   
    


# /*!
#    @function latest_prompt
#    @param [optional] $latest_prompt string. If not provide, the sub will get the 'LatestPrompt'. Otherwise, 'LatestPrompt' will be set as input. 
#    @return current LatestPrompt
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Get or set LatestPrompt which reflect where we are in the CLI shell.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $LatestPrompt=$cli1->latest_prompt;
#        <pre> my $LatestPrompt=$cli1->latest_prompt('login#'); </pre>
#
# */
sub latest_prompt{
      my $self = shift;
    if (@_) { $self->{LatestPrompt} = shift }
    return $self->{LatestPrompt};
    }   
    


# /*!
#    @function handle
#    @param none 
#    @return current CLI operation handle
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Set/get current CLI operation handle. Not suggest to set handle unless you know what you are doing.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $handle=$cli1->handle;</pre>
#
# */
sub handle{
      my $self = shift;
      if(@_){
        $self->{Handle}=shift;
        }
    return $self->{Handle};
    }   



#    @function if_new_handle
#    @param 0/1 - 0 means the current handle is not a new created. 1 - means current handle is new created or need create a new 
#    @return current CLI operation handle
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>   Set/get attribute {IfNewHandle}. this method is private, no need go into API online doc.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $result=$cli1->if_new_handle; </pre>
#


sub if_new_handle{
      my $self = shift;
      if(@_){
        $self->{IfNewHandle}=shift;
        }
    return $self->{IfNewHandle};
    }   



#    @function handle_type
#    @param console/ssh: console - the expect handle was created via console, ssh - the handle was created via ssh 
#    @return current CLI operation handle type
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Set/get attribute {HandleType}. This method is private, no need go into API online doc.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $HandleType=$cli1->handle_type.   
#        <pre> my $HandleType=$cli1->handle_type('console').</pre>
#

sub handle_type{
      my $self = shift;
      if(@_){
        $self->{HandleType}=shift;
        }
    return $self->{HandleType};
    }   


#    @function connect_num
#    @param 
#    @return current CLI operation handle
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Set/get attribute {ConnectNum}. This method is private, no need go into API online doc. </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $ConnectNum=$cli1->connect_num;   </pre>
#
sub connect_num{
      my $self = shift;
      if(@_){
        $self->{ConnectNum}=shift;
        }
    return $self->{ConnectNum};
    }   

# /*!
#    @function model
#    @param string [OPTIONAL] model value you should set.
#    @return value of attribute {Model}
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Set/get attribute {Model}. </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001',Model=>'XTM510'); 
#        <pre> my $model=$cli1->model;  </pre>
#
# */
sub model{
      my $self = shift;
      if(@_){
        $self->{Model}=shift;
        }
    return $self->{Model};
    }   


# /*!
#    @group WG Linux Shell operation 
# */

# /*!
#    @function connect_serial
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  This method is for 'console' mode only. Create a new connection with serial port of XTM via SN,  
#    <pre>  don't go into WG Linux shell or CLI prompt when connect successful,
#    <pre>  it would stay in 'login' prompt. If ok, then we can use sub 'handle' to get the serial port operation handle.
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $ConnectSerial=$cli1->connect_serial; </pre>
#
# */
sub connect_serial{
      my $self = shift;
      my $exp = new Expect; 
      my $timeout=30;
      my $rootuser;
      my $hostip;
      my $sshport;
      my $snip;
      my $snport;
      my $result='ok';
      my $connectnum;
      
      if (defined($self->handle)){
            print "try to connect_serial, found there is already a connection handle exist: $self->handle return nok\n";
            return 'nok';
        }
    
    if ($self->mode eq 'ssh'){
        print "The method connect_serial is for console mode only. current mode is set as ssh...return nok\n";
        return 'nok';
        } 
    
    elsif($self->mode eq 'console'){
        
            #if use console mode, sn_ip and sn_port are mandatory
        if( (!defined($self->sn_ip))|| (!defined($self->sn_port)) ){
                print "try connect_serial, sn_ip or sn_port does not have value. Return nok\n";
                return 'nok';
            } 
        
        $snip = $self->sn_ip;
        $snport= $self->sn_port;
            $exp = Expect->spawn("telnet $snip $snport")
            or return "nok";
            $self->handle_type('console');
            
            $connectnum = $self->connect_num;
            $self->connect_num($connectnum+1);
            print "CLI DEBUG,console connect num:",$self->connect_num,"\n";

        
        my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,

                         [
                          qr'Escape character is',
                          sub{
                            my $fh = shift;
                            sleep 1;
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
                         [
                #qr 'WG', #DEBUG MATCH
                qr /WG.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],

               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("\r");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-Connect-Serial-TIMEOUT\n";
                 
                }
               ],
               
               '-re',qr/[Ll]ogin:/, #' wait for shell prompt, then exit expect
              );

      $self->latest_prompt($exp->match);
      if($result eq 'nok'){
                return "$result";
              }
             else{
              $self->{Handle}=$exp;
              #print "handle: ",$exp," == ",$self->handle,"\n";
                return 'ok';
            }
    }
    else{
        print "try to connect_serial, get unknow mode: $self->mode return nok\n";
        return 'nok';   
    }
    
    }   

#####Remove some comments for generate online document.remove by Sam at 2011/01/26

# /*!
#    @function connect_shell
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> Create a new connection with XTM via ssh or console, and go into WG Linux shell.  
#    <pre> If ok, then we can use sub 'handle' to get the shell operation handle.
#    <pre> Usually WG linux shell can be accessed only with debug image. 
#    <pre> This method always try to estabish a new connection handle. 
#    <pre> Call this method can not switch an existing connection from current status to Linux shell status. 
#    <pre> To do that you need to disconnect current connection first.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');  
#        <pre> my $ConnectShell=$cli1->connect_shell;   </pre>
#
# */
sub connect_shell{
      my $self = shift;
      my $exp = new Expect; 
      my $timeout=30;
      my $rootuser;
      my $hostip;
      my $sshport;
      my $snip;
      my $snport;
      my $result='ok';
      my $connectnum;
      
      if (defined($self->handle)){
            print "XtmCli:connect_shell Handle already exist: $self->handle, it was created via $self->handle_type \n";
            $self->if_new_handle(0); 
            #means not need new handle once more, or means it is not a new handle:)
      }
    
    if ($self->mode eq 'ssh'){
        #if use ssh mode, host_ip and ssh_port are mandatory
        if( (!defined($self->host_ip)) || (!defined($self->ssh_port)) ){
                print "try connect_shell with ssh mode, host_ip or ssh_port does not have value.Return nok\n";
                return 'nok';
            } 
        
        $rootuser= $self->root_user;
        $hostip = $self->host_ip;
        $sshport= $self->ssh_port;
        if(($self->if_new_handle)||($self->handle_type ne $self->cli_mode)){
                $exp = Expect->spawn("ssh $rootuser\@$hostip -p $sshport")
                or return "nok";
                $self->handle_type('ssh');
                $self->if_new_handle(1);
        }
        else{
            $exp = $self->handle;   
            $result = $self->switch_to_shell;
            $self->if_new_handle(0);
            return $result;
            
        }
        
        my $ifrespawn=0;
        my $temp=0;
        #$exp->debug(3);
        $exp->expect($timeout,
       [
        qr/\(yes\/no\)/,
        sub {
          my $fh = shift;
          $fh->send("yes\r");
          exp_continue;
        }
       ],
         
       [
        qr/[Pp]assword:/,
        sub {
            my $fh = shift;
            #print "fh: $fh\n";
            if($temp == 0){
              my $passwd=$self->root_passwd;
              $fh->send("$passwd\r");
              $temp +=1;
              #print "(temp is $temp)\n";
              exp_continue;
            }
            else{
                print "(WATS try to hard_close connection)\n";
                $result = 'nok';
                $fh->hard_close();
                }
                    
        }
       ],
       [
        eof =>
        sub {
            print "meet EOF\n";
          $result='nok';
        }
       ],
       [
            qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED\!/,
            sub {
                print "[WATS:connect_shell via ssh, need to remove host file known_hosts]\n";
                ###########add by lizhi 2011-11-07###############
                $buf = `rm /home/automation/.ssh/known_hosts`;
                $buf = `rm /root/.ssh/known_hosts`;
                #################################################
                $ifrespawn=1;
                $exp->hard_close;
                }
         ],
       [
        timeout =>
        sub {
            $result='nok';
            print "connect_shell:WATS go into timeout..\n";
            $exp->hard_close();
            print "WATS-CLI-TIMEOUT\n";
         
        }
       ],
       #'-re',qr/$rootuser\@.*#/, #' wait for shell prompt, then exit expect
       '-re',qr/\@.*#/,
      );
            
            #Nicky - to solve the problem of ssh identification change problem - Start      
      if($ifrespawn eq 1){
        
              $temp=0;
                    $exp = Expect->spawn("ssh $rootuser\@$hostip -p $sshport")or return "nok";
                #$exp->debug(3);
                $exp->expect($timeout,
               [
                qr/\(yes\/no\)/,
                sub {
                  my $fh = shift;
                  $fh->send("yes\r");
                  exp_continue;
                }
               ],
                 
               [
                qr/[Pp]assword:/,
                sub {
                    my $fh = shift;
                    #print "fh: $fh\n";
                    if($temp == 0){
                      my $passwd=$self->root_passwd;
                      $fh->send("$passwd\r");
                      $temp +=1;
                      #print "(temp is $temp)\n";
                      exp_continue;
                    }
                    else{
                        print "(WATS try to hard_close connection)\n";
                        $result = 'nok';
                        $fh->hard_close();
                        }
                            
                }
               ],
               [
                eof =>
                sub {
                    print "meet EOF\n";
                  $result='nok';
                }
               ],
               [
                timeout =>
                sub {
                    $result='nok';
                    print "connect_shell:WATS go into timeout..\n";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               #'-re',qr/$rootuser\@.*#/, #' wait for shell prompt, then exit expect
               '-re',qr/\@.*#/,
              );

        
      }
      #Nicky - to solve the problem of ssh identification change problem - End 
      
      $self->latest_prompt($exp->match);
      if($result eq 'nok'){
                return "$result";
              }
             else{
             
              $self->{Handle}=$exp;
              #print "handle: ",$exp," == ",$self->handle,"\n";
                return 'ok';
            }
      
    } 
    
    elsif($self->mode eq 'console'){
            #if use console mode, sn_ip and sn_port are mandatory
        if( (!defined($self->sn_ip)) || (!defined($self->sn_port)) ){
                print "try connect_shell with console mode, sn_ip or sn_port does not have value. Return nok\n";
                return 'nok';
            } 
        
        $rootuser= $self->root_user;
        $rootpasswd = $self->root_passwd;
        $snip = $self->sn_ip;
        $snport= $self->sn_port;
        if(($self->if_new_handle)||($self->handle_type ne $self->cli_mode)){
                $exp = Expect->spawn("telnet $snip $snport")
                or return "nok";
                $self->handle_type('console');
                $self->if_new_handle(1);
                
                $connectnum = $self->connect_num;
            $self->connect_num($connectnum+1);
            print "CLI DEBUG,console connect num:",$self->connect_num,"\n";
                
        }
        else{
            $exp = $self->handle;   
            $result=$self->switch_to_shell;
            $self->if_new_handle(0);
            return $result;
        }
        
        my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,

                         [
                          qr'Escape character is',
                          sub{
                            my $fh = shift;
                            sleep 1;
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
                         [
                #qr 'WG', #DEBUG MATCH
                qr /WG.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
                         [
                qr /Login incorrect/,
                sub {
                    print "login incorrect...";
                    $result="nok";
                    $exp->hard_close();
                 
                }
               ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  $fh->send("$rootuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("$rootpasswd\r");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               
               #'-re',qr/$rootuser\@.*#/, #' wait for shell prompt, then exit expect
               '-re',qr/\@.*#/,
              );

      
      if($result eq 'nok'){
                return "$result";
              }
             else{
              $self->{Handle}=$exp;
              #print "handle: ",$exp," == ",$self->handle,"\n";
                return 'ok';
            }
    }
    else{
        print "try to connect_shell, get unknow mode: $self->mode return nok\n";
        return 'nok';   
    }
    
    }   


#/*!
#    @function check_ipsec_sa
#    @param  parametes should be a list of anonymous hashes. Each hash should contains the following key:
#    @param  local_gateway   [REQUIRED] specify local gateway. 
#    @param  remote_gateway  [REQUIRED] specify remote gateway.
#    @param  type            [REQUIRED] specify type of tunnel, esp or ah.
#    @param  dir             [REQUIRED] specify direction of tunnel.
#    @param  local_tunnel    [REQUIRED] specify local subnet or local host or local host range.
#    @param  local_tunnel    [REQUIRED] specify remote subnet or remote host or remote host range 
#    @return return a string \$result: 
#                    $result is check result:ok/nok;
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>  check ipsec sa is matched with given info.</pre>
#    <h2>Example:</h2>
#    <pre>  my $mypc = new LinuxPC(HostIP=>'10.138.1.84', UserName=>'automation', Password=>'password', RootPassword=>'password');
#    <pre>  my \$check = check_ipsec_sa(
#    <pre>      {
#    <pre>          local_gateway =>$dut2_external_ip, 
#    <pre>          remote_gateway =>$dut1_external_ip , 
#    <pre>          type=> "ESP", 
#    <pre>          dir => "OUT", 
#    <pre>          local_tunnel =>$dut2_trusted_subnet, 
#    <pre>          remote_tunnel =>$dut1_trusted_subnet
#    <pre>      },{
#    <pre>          local_gateway =>$dut1_external_ip  , 
#    <pre>          remote_gateway =>$dut2_external_ip , 
#    <pre>          type=> "ESP", 
#    <pre>          dir => "IN", 
#    <pre>          local_tunnel =>$dut1_trusted_subnet, 
#    <pre>          remote_tunnel =>$dut2_trusted_subnet
#    <pre>      }
#    <pre>  );
#    <pre>  print $result[0]; #result of the command:'ok'/'nok';
#    <pre>  print $result[1]; #stdout of the command;</pre>
#
# */
sub check_ipsec_sa {
    my $self = shift;
    my @args_array = @_;
    my @check;
    my @ret = $self->connect_cli;
     if ($ret[0] eq 'nok') {
          LogInfo("connect to failed.");
          return "nok"
     }
    my $if_new_handle = $self->if_new_handle;
    # obtain dut's ipsec sa
    @ret = $self->cmd("diagnose vpn \"/ipsec/sa/list\"");
    if ($ret[0] eq 'nok') {
        LogInfo("execute diagnose vpn command failed.");
        return "nok"; ## config failed
    }
    
    # delete wg prompt
    $ret[1] =~ s/wg\#\w+\n//i;
    $ret[1] =~ s/wg\#\n//i;

    # split output by "\n\n" to tunnel entries.
    my @tmp = split(/\n\n/,$ret[1]);
    foreach my $raw_output (@tmp) {
        foreach my $args (@args_array) {
        if(!exists($args->{mode})){
           $args->{mode} = 'TUNNEL';
        }
        print("\n----- check_ipsec_sa -----\n");
        print("Gateway:\n");
        print("Local: [$args->{local_gateway}], Remote: [$args->{remote_gateway}]\n");
        print("Type: [$args->{type}] , Direction: [$args->{dir}]\n");
        print("Mode: [$args->{mode}]:\n");
        print("Local: [$args->{local_tunnel}], Remote: [$args->{remote_tunnel}]\n");
        print("\n----- END -----\n");
            if ($raw_output !~ /SRC:\s*$args->{local_gateway}\s+DST:\s*$args->{remote_gateway}\s+$args->{type}\s+$args->{mode}\s+$args->{dir}/i
                or $raw_output !~ /src:\s*$args->{local_tunnel}\s+dst:\s*$args->{remote_tunnel}/i) {
                return "nok";
            }
        }
    }
    if($if_new_handle){
        $self->disconnect;
    }
    else{
        $self->switch_to_cli;
    }
   
    return "ok";
}


# /*!
#    @group XTM CLI basic operation
# */


# /*!
#    @function admin_logoff
#    @param none
#    @return ok/nok. ok means admin logoff successfully, nok means operation fail
#    @discussion 
#    <h2>Description:</h2> 
#    <pre> log off current admin user. This operation is base on linux shell, so only debug build can support this method.      </pre>
#    <h2>example:</h2>
#    <pre> #test if CLI ok via ssh  
#    <pre> my $mycli=new XtmCli(HostIP=>'192.168.1.2'); 
#    <pre> my $result=$mycli->admin_logoff; 
#    <pre> print $result ;
# */
sub admin_logoff{
    my $self=shift;
    my $result=$self->connect_shell;
    
    my $if_new_handle = $self->if_new_handle;
    my @output;
    if ($result eq 'nok'){
        print "XtmCli: admin_logoff method, can not connect to shell...return nok.\n";
        return 'nok';
    }
    else{
        @output = $self->cmd("wgcmd action \/authentication\/logoff type=6");
        #print "XXXXXXXXXXXXXXXxoutput:\n$output[1] XXXXXX\n";
        if ($output[1] =~ m/logoff_list/){
            $result = 'ok';
            }
        else{
            $result = 'nok';
            }
        
        }
    
    if($if_new_handle){
        $self->disconnect;
    }
    else{
        $self->switch_to_shell;
        }
        
    return $result;
    
    }

# /*!
#    @function connect_cli
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  If there is no expect session, create a new connection with XTM via ssh or console, and go into CLI WG# prompt. 
#        <pre>  If ok, then we can use sub 'handle' to get the shell operation handle. </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001'); 
#        <pre> my $ConnectCli=$cli1->connect_cli;   </pre>
#
# */
sub connect_cli{
      my $self = shift;
      my $exp = new Expect; 
      my $timeout=40;
      my $adminuser;
      my $adminpasswd;
      my $hostip;
      my $sshport;
      my $snip;
      my $snport;
      my $result='ok';
      my $connectnum;
      my $rootuser= $self->root_user;
      
      if (defined($self->handle)){
            print "XtmCli:connect_cli Handle already exist:", $self->handle," it was created via ", $self->handle_type," \n";
            $self->if_new_handle(0); 
            #means not need new handle once more, or means it is not a new handle:)
        }
      else{
            print "XtmCli:connect_cli handle is undef...need to create one\n";
            $self->if_new_handle(1);
        }
    
    if ($self->mode eq 'ssh'){
        #if use ssh mode, host_ip and ssh_port are mandatory
        if( (!defined($self->host_ip)) || (!defined($self->ssh_port)) ){
                print "try connect_cli with ssh mode, host_ip or ssh_port does not have value.Return nok\n";
                return 'nok';
            } 
        
        $adminuser= $self->admin_user;
        $adminpasswd=$self->admin_passwd;
        $hostip = $self->host_ip;
        $sshport= $self->ssh_port;
        my $buf;
        my $ifrespawn=0;
        #print "if_new_handle:",$self->if_new_handle,"\n";
        #print "handl_type:",$self->handle_type," cli_mode:",$self->cli_mode," \n";
        if(($self->if_new_handle)||($self->handle_type ne $self->cli_mode)){
            $exp = Expect->spawn("ssh $adminuser\@$hostip -p $sshport") or return "nok";
            $self->handle_type('ssh');
            $self->if_new_handle(1);
        }
        else{
            $exp = $self->handle;   
            $result = $self->switch_to_cli;
            $self->if_new_handle(0);
            return $result;
        }
        
        my $temp=0;
        #$exp->debug(3);
        $exp->expect($timeout,
     
       [
        qr/\(yes\/no\)/,
        sub {
          my $fh = shift;
          $fh->send("yes\r");
          exp_continue;
        }
       ],
         
       [
        qr/[Pp]assword:/,
        sub {
            my $fh = shift;
            #print "fh: $fh\n";
            if($temp == 0){
              $fh->send("$adminpasswd\r");
              $temp +=1;
              #print "(temp is $temp)\n";
              exp_continue;
            }
            else{
                print "(WATS try to hard_close connection)\n";
                $result = 'nok';
                $fh->hard_close();
                }
                    
        }
       ],
       [
            qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED\!/,
            sub {
                print "[WATS: need to remove host file known_hosts]\n";
                ########################lizhi 2012-03-26################
                #$buf = `rm ~/.ssh/known_hosts`;
                $buf = `rm /home/automation/.ssh/known_hosts`;
                $buf = `rm /root/.ssh/known_hosts`;
                ########################################################
                $ifrespawn=1;
                $exp->hard_close;
                }
         ],
         [
            qr/\%Error: Login failed. \'admin\' is currently logged in/,
            sub {
                print "[WATS: try to force logoff admin...]\n";
                $self->admin_logoff;
                $ifrespawn=1;
                $exp->hard_close;
                }
         ],
         [
            qr/Connection reset by peer/,
            sub {
                print "[WATS: connect close by box...]\n";
                $ifrespawn=1;               
                }
         ],
       [
        eof =>
        sub {
            print "meet EOF\n";
          $result='nok';
        }
       ],
       [
        timeout =>
        sub {
            $result='nok';
            print "connect_cli:WATS go into timeout..\n";
            $exp->hard_close();
            print "WATS-CLI-TIMEOUT\n";
         
        }
       ],
       #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
       '-re', qr/(WG#|WG>|WG<master>#|WG<non-master>#)/,
       
      );
      
      ###Nicky to solve the ssh identification change problem - Start
      if($ifrespawn eq 1){
      $exp = Expect->spawn("ssh $adminuser\@$hostip -p $sshport") or return "nok";  
      $temp=0;
        #$exp->debug(3);
        $exp->expect($timeout,
     
       [
        qr/\(yes\/no\)/,
        sub {
          my $fh = shift;
          $fh->send("yes\r");
          exp_continue;
        }
       ],
         
       [
        qr/[Pp]assword:/,
        sub {
            my $fh = shift;
            #print "fh: $fh\n";
            if($temp == 0){
              $fh->send("$adminpasswd\r");
              $temp +=1;
              #print "(temp is $temp)\n";
              exp_continue;
            }
            else{
                print "(WATS try to hard_close connection)\n";
                $result = 'nok';
                $fh->hard_close();
                }
                    
        }
       ],
       
       [
        eof =>
        sub {
            print "meet EOF\n";
          $result='nok';
        }
       ],
       [
        timeout =>
        sub {
            $result='nok';
            print "connect_cli:WATS go into timeout..\n";
            $exp->hard_close();
            print "WATS-CLI-TIMEOUT\n";
         
        }
       ],
       #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
       '-re', qr/(WG#|WG>|WG<master>#|WG<non-master>#)/,
       
      );

        
        }
      ###Nicky to solve the ssh identification change problem - End
      
      $self->latest_prompt($exp->match);
      if($result eq 'nok'){
                return "$result";
              }
             else{
              $self->{Handle}=$exp;
              #print "handle: ",$exp," == ",$self->handle,"\n";
                return 'ok';
            }
      
    } 
    
    elsif($self->mode eq 'console'){
            #if use console mode, sn_ip and sn_port are mandatory
        if((!defined($self->sn_ip))||(!defined($self->sn_port)) ){
                print "try connect_cli with console mode, sn_ip or sn_port does not have value. Return nok\n";
                return 'nok';
            } 
        
        $adminuser= $self->admin_user;
        $adminpasswd = $self->admin_passwd;
        $snip = $self->sn_ip;
        $snport= $self->sn_port;
        if(($self->if_new_handle)||($self->handle_type ne $self->cli_mode)){
                $exp = Expect->spawn("telnet $snip $snport") or return "nok";
                $self->handle_type('console');
                $self->if_new_handle(1);
                
            $connectnum = $self->connect_num;
            $self->connect_num($connectnum+1);
            print "CLI DEBUG,console connect num:",$self->connect_num,"  snip:",$snip,"  sn port:",$snport,"\n";

        }
        else{
            $exp = $self->handle;
            $result = $self->switch_to_cli;
            $self->if_new_handle(0);
                return $result;

        }

            #try 1st time
            my $ifrespawn=0;
            my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,

                         [
                          qr'Escape character is',
                          sub{
                            my $fh = shift;
                            sleep (1);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminpasswd\n");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               [
                qr /Login incorrect/,
                sub {
                    print "[get login incorrect...]";
                    $result="nok";
                    $exp->hard_close();
                 
                }
               ],
               [
                qr/WG\(.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                #qr/$rootuser\@.*#/,
                qr/\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("cli\r");
                  exp_continue;
                }
               ],
               [
                            qr/\%Error: Login failed. \'admin\' is currently logged in/,
                            sub {
                                
                                print "[WATS: try to force logoff admin...]\n";
                                $self->{Handle} = $exp;
                                my $ret_s = $self->switch_to_shell;
                                if($ret_s eq 'ok'){
                                    $self->cmd("wgcmd action \/authentication\/logoff type=6"); 
                                }
                                else{
                                    print "[WATS:can not go into shell..]\n";   
                                    $result = 'nok';
                                        $exp->hard_close();
                                        print "[admin is currently used by another session,but can not logoff]\n";
                                    
                                }
                                    $exp->send("\r");
                                    exp_continue;
                                    
                            }
                         ],
               
               
               #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
                             '-re', qr/(WG#|WG<master>#|WG<non-master>#)/,
              );

      $self->latest_prompt($exp->match);
      if($result eq 'nok'){
            $self->{Handle}=undef;
                return "$result";
              }
             else{
              $self->{Handle}=$exp;
              #print "handle: ",$exp," == ",$self->handle,"\n";
                return 'ok';
            }
    }
    else{
        print "try to connect_cli, get unknow mode: $self->mode return nok\n";
        return 'nok';   
    }
    
    }   



# /*!
#    @function if_admin_login
#    @param none 
#    @return string of ok|nok|unknown. ok means there is definitly already an admin login. nok means there is definitly not an admin login. unknown means the judgement process does not procceed successfully.
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  Check if XTM box currently has already a session login by admin user. 
#        <pre>  If there is already an admin login then the box will not allow 2nd admin login session. 
#        <pre>  CLI is used for this test. Whether use CLI via console or ssh depend on how user set the CLIMode.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->if_admin_login;    </pre>
#
# */
sub if_admin_login{
      my $self = shift;
      my $exp = new Expect; 
      my $timeout=30;
      my $adminuser;
      my $adminpasswd;
      my $hostip;
      my $sshport;
      my $snip;
      my $snport;
      my $result='unknown';
      my $connectnum;
      
      my $latest_prompt =$self->latest_prompt;
      if (defined($self->handle)&&($latest_prompt =~ m/WG.*\#/)) {
            print "call if_admin_login, found object already with WG.*# prompt. This implies admin must be login already\n";
            return 'ok';
        }
    
    if ($self->mode eq 'ssh'){
        #if use ssh mode, host_ip and ssh_port are mandatory
        if((!defined($self->host_ip))||(!defined($self->ssh_port))){
                print "try if_admin_login with ssh mode, host_ip or ssh_port does not have value.Return unknown\n";
                return 'unknown';
            } 
        
        $adminuser= $self->admin_user;
        $adminpasswd=$self->admin_passwd;
        $rootuser=$self->root_user;
        $rootpasswd=$self->root_passwd;

        $hostip = $self->host_ip;
        $sshport= $self->ssh_port;
            $exp = Expect->spawn("ssh $adminuser\@$hostip -p $sshport")
            or return "unknown";
        
        my $temp=0;
        #$exp->debug(3);
        $exp->expect($timeout,
       [
        qr/\(yes\/no\)/,
        sub {
          my $fh = shift;
          $fh->send("yes\r");
          exp_continue;
        }
       ],
         
       [
        qr/[Pp]assword:/,
        sub {
            my $fh = shift;
            #print "fh: $fh\n";
            if($temp == 0){
              $fh->send("$adminpasswd\r");
              $temp +=1;
              #print "(temp is $temp)\n";
              exp_continue;
            }
            else{
                print "(WATS try to hard_close connection)\n";
                $result = 'unknown';
                $fh->hard_close();
                }
                    
        }
       ],
       [
        eof =>
        sub {
            print "meet EOF\n";
          $result='unknown';
        }
       ],
       [
        timeout =>
        sub {
            $result='unknown';
            print "if_admin_login:WATS go into timeout..\n";
            $exp->hard_close();
            print "WATS-CLI-TIMEOUT\n";
         
        }
       ],
       [
         #qr/\Error: Login failed. \'admin\' is currently logged in/,
         #qr/WG#/,  #DEBUG MATCH
         qr /WG.*#/,
                 sub {
                      $result = 'nok';
                      $exp->send("exit\r");
                      $exp->expect( $timeout,
                                                [
                                                    #qr/$rootuser\@.*#/,
                                                    qr/\@.*#/,
                                                            sub{
                                                                $exp->send("exit\r");
                                                                exp_continue;
                                                                }
                                            ],
                                                [qr/[Ll]ogin:/,
                                                    sub{
                                                        $exp->hard_close();
                                                        }
                                                ],
                                                [timeout=>
                                                     sub{
                                                            print "[wats:try to if_admin_login(ssh),self session does not logout]";
                                                            $result='unknown';
                                                            $exp->hard_close();
                                                        }
                                                ]
                                            );
                            $exp->hard_close();
                            print "[I can enter CLI, seems admin was not been used by another session]\n";
                     }
         ],
         [
         qr/\Error: Login failed. \'admin\' is currently logged in/,
         sub {
                $result = 'ok';
                            $exp->hard_close();
                            print "[admin is currently used by another session]\n";
              }
         ]
       #'-re',qr/WG#/, #' wait for shell prompt, then exit expect
       
      );
      
            return $result;
      
    } 
    
    elsif($self->mode eq 'console'){
            #if use console mode, sn_ip and sn_port are mandatory
        if( (!defined($self->sn_ip)) || (!defined($self->sn_port))){
                print "try if_admin_login with console mode, sn_ip or sn_port does not have value. Return unknown\n";
                return 'unknown';
            } 
        
        $adminuser= $self->admin_user;
        $adminpasswd = $self->admin_passwd;
        $rootuser=$self->root_user;
        $rootpasswd=$self->root_passwd;
        $snip = $self->sn_ip;
        $snport= $self->sn_port;
            $exp = Expect->spawn("telnet $snip $snport")
            or return "unknown";
            
            $connectnum = $self->connect_num;
            $self->connect_num($connectnum+1);
            print "CLI DEBUG,console connect num:",$self->connect_num,"\n";
        
        my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,

                         [
                          qr'Escape character is',
                          sub{
                            my $fh = shift;
                            sleep (1);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminpasswd\n");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="unknown";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="unknown";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               [
                qr /Login incorrect/,
                sub {
                    print "[get login incorrect...]";
                    $result="unknown";
                    $exp->hard_close();
                 
                }
               ],
               [
                qr/WG\(.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                #qr/$rootuser\@.*#/,
                qr/\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("cli\r");
                  exp_continue;
                }
               ],
               [
                         #qr/\Error: Login failed. \'admin\' is currently logged in/,
                         #qr/WG#/, #DEBUG MATCH
                         qr/WG.*#/,
                         sub {
                                $result = 'nok';
                                $exp->send("exit\r");
                                $exp->expect($timeout,
                                                        [
                                                            #qr/$rootuser\@.*#/,
                                                            qr/\@.*#/,
                                                            sub{
                                                                $exp->send("exit\r");
                                                                exp_continue;
                                                                }
                                                        ],
                                                        [qr/[Ll]ogin:/,
                                                            sub{
                                                                $exp->hard_close();
                                                                }
                                                            ],
                                                            [timeout=>
                                                             sub{
                                                                    print "[wats:try to if_admin_login,self session does not logout]";
                                                                    $result='unknown';
                                                                    $exp->hard_close();
                                                                }]
                                                            );
                                            $exp->hard_close();
                                            print "[I can enter CLI, seems admin was not been used by another session]\n";
                              }
                        ],
                        [
                         qr/\Error: Login failed. \'admin\' is currently logged in/,
                         sub {
                                $result = 'ok';
                                            $exp->hard_close();
                                            print "[admin is currently used by another session]\n";
                              }
                        ]
              );

      
                return $result;
    }
    else{
        print "try to if_admin_login, get unknow cli mode: $self->mode . return unknown\n";
        return 'unknown';   
    }
    
    }   

# /*!
#    @function switch_to_cli
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Switch to cli prompt 'WG#' with current handle and current mode, without establish new cli connection. 
#    <pre>  Usually this method used after connect_shell or after some cli execution, and want to enter or back to cli prompt WG#.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->switch_to_cli; </pre>
#
# */
sub switch_to_cli{
      my $self = shift;
      my $adminuser = $self->admin_user;
      my $adminpasswd=$self->admin_passwd;
      my $rootuser=$self->root_user;
      my $rootpasswd=$self->root_passwd;
      my $exp = $self->handle; 
      my $timeout=30;
      my $result='ok';
      
      if (!defined($self->handle)){
            print "XtmCli:switch_to_cli Handle undef, can not do switch,return nok\n";
                return 'nok';
        }
    
    if ($self->mode ne $self->handle_type){
        print "XtmCli:switch_to_cli: expected cli mode:", $self->mode," does not match handle type:",$self->handle_type," Can not do switch, you may need try connect_cli. return nok\n";
        return 'nok';
    }
    
    $exp->send("\r");   
      my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("$adminpasswd\n");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    print "switch_to_cli: WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               [
                qr /Login incorrect/,
                sub {
                    print "[get login incorrect...]";
                    $result="nok";
                }
               ],
               [
                qr/WG\(.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                qr/$rootuser\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("cli\r");
                  exp_continue;
                }
               ],
               [
                qr/\%Error\: Login failed/,
                sub {
                    $result = 'nok';
                                    print "switch_to_cli:[admin is currently used by another session]\n";
                }
               ],
               
               
               #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
                             '-re', qr/(WG#|WG<master>#|WG<non-master>#)/,
              );

        $self->latest_prompt($exp->match);
        
      return $result;
    
    }   


# /*!
#    @function switch_to_shell
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  Switch to cli prompt 'root\@' with current handle and current mode, without establish new shell connection. 
#    <pre>  Usually this method used after connect_cli or after some cli/shell execution, and want to enter or back to shell prompt 'root\@'.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->switch_to_shell;   </pre>
#
# */
sub switch_to_shell{
      my $self = shift;
      my $adminuser = $self->admin_user;
      my $adminpasswd=$self->admin_passwd;
      my $rootuser=$self->root_user;
      my $rootpasswd=$self->root_passwd;
      my $exp = $self->handle; 
      my $timeout=30;
      my $result='ok';
      
      if (!defined($self->handle)){
            print "XtmCli:switch_to_shell Handle undef, can not do switch,return nok\n";
                return 'nok';
        }
    
    if ($self->mode ne $self->handle_type){
        print "XtmCli:switch_to_shell: expected cli mode:", $self->mode," does not match handle type:",$self->handle_type," Can not do switch, you may need try connect_shell. return nok\n";
        return 'nok';
    }
    
    $exp->send("\r");   
        my $temp=0;
        #$exp->debug(3);
          $exp->expect($timeout,

                         [
                #qr 'WG', #DEBUG MATCH
                qr /WG.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
                         [
                qr /Login incorrect/,
                sub {
                    print "login incorrect...";
                    $result="nok";
                 
                }
               ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  $fh->send("$rootuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  $fh->send("$rootpasswd\r");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    print "switch_to_shell:WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               
               #'-re',qr/$rootuser\@.*#/, #' wait for shell prompt, then exit expect
               '-re',qr/\@.*#/,
              );


        $self->latest_prompt($exp->match);
        
      return $result;
    
    }   
    


# /*!
#    @function disconnect
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Exit from shell or cli and disconnect the connection and release handle.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->disconnect;    </pre>
#
# */
sub disconnect{
    $self = shift;
    my $result = 'ok';
    my $handle = $self->handle;
    my $rootuser = $self->root_user;

    #print "before disconnect, handle=$handle \n";
    if(!defined($handle)){
        print "Current handle is undef, disconnect need do nothing. Return ok\n";
        return 'ok';
        }
        
    my $timeout=30;
    $handle->send("\r");
    $handle->expect($timeout,
                 [
                qr /WG.*#$/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                #qr /$rootuser\@.*#/,
                qr /\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
                 [
                qr 'login:',
                sub {
                    print "[wats:close cli or shell handle]";
                  $handle->hard_close();
                }
               ],
               [
                timeout =>
                sub {
                    print "[CLI disconnect wait TimeOut]\n";
                  $handle->hard_close();
                  #$result="nok";
                  
                }
               ],
               [
                        eof =>
                        sub {
                            print "meet EOF,for disconnect this should be good\n";
                          $result='ok';
                        }
                       ],                   
    );

    #anyway, reset the handle to undef
    $self->{Handle} = undef;
    #print "end disconnect, handle=", $self->handle," \n";
    
  #nicky 20090607 add, important,can avoid lot of recover error
  #sleep 10;
  print "[","$result","]\n";
  return $result;

    }
    
    
# /*!
#    @function cmd
#    @param $cmd Cli or Shell command(s) 
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Excute cli commands with current handle. you can input params as much as you need, 
#    <pre>  they will be executed by order. This method does not do the connect cli or shell action, 
#    <pre>  so before use this method, you need to call connect_cli or connect_shell to get handle. 
#    <pre>  This method does not disconnect cli or shell handle, you need to control disconnect by yourself.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->cmd;   </pre>
#
# */    

sub cmd{
        $self=shift;
        my $handle=$self->handle;
        my $rootuser=$self->root_user;
        my $timeout=150;
        my $cmd_output=$self->handle->match; #just a trick, let output 1st line show shell prompt
        my @result=('ok',$cmd_output);
        
        if(!defined($handle)){
                print "Handle is undef, can not execute any cmd. Return nok.\n";
            }
        #$handle->debug(3);
        
        #sleep corresponding time 
        my $cm = new ConfigurationManager;
        my $series;
        my $model=$self->model;
        if($model =~ m/[XxTtMm]2050/){
            $series = 'xtm20series';
        }
        elsif($model =~ m/[XxTtMm]2/){
            $series = 'xtm2series';
        }
        elsif(lc($model) =~ m/xtm5|xtm3/){
            $series = 'xtm5series';
        }
        elsif(lc($model) =~ m/xtm8/){
            $series = 'xtm8series';
        }
        elsif(lc($model) =~ m/xtm10/){
            $series = 'xtm10series';
            $timeout=300;
        }
            
        my $interval = $cm->getCliCmdInterval($series);

        foreach my $cmd (@_){
              ##2011-07-18 delete by Arlen: begain
                ##if($cmd eq '?'){
                ##  #this is important,fix the bug of output not completed problem. as cmd ? does not need input \r or \n
                ##  $handle->send("$cmd");
                ##}
                ##2011-07-18 delete by Arlen: end
                
                ##2011-07-18 add by Arlen: begain
                if($cmd =~ m/\?/ ) {
           $handle->send("$cmd");
        } elsif ($cmd =~ m/\\T/i) {
           #$cmd =~ s/\t//;
           $handle->send("$cmd");
        ##2011-07-18 add by Arlen: end
        } else{ 
                    $handle->send("$cmd\r");
              }
                my ($matched_pattern_position,
                     $error,
                     $successfully_matching_string,
                     $before_match,
                     $after_match)
                     =$handle->expect($timeout,
                                        [
                                          '-re', qr/WG.*#/,
                                          sub{
                                             #print "match.....\n";
                                             $self->latest_prompt($handle->match);
                                             #print "[WATS:sleep $interval sec]";
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         [
                                          #'-re', qr/$rootuser\@.*#/,                                         
                                          '-re', qr/\@.*#/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         #following block is to fix restore factory-default can not input 'yes' problem
                                         [
                                          '-re', qr/\(yes or no\)/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         ###add by gangKong to fix the problem of "Failed to get interface runtime information"
                                          
                                         
                                         #following block is to fix reboot cmd does not reflect 'ok' result problem
                                         [
                                          '-re', qr/Rebooting/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                                sleep ($interval);
                                              
                                            }
                                         ],

                                         ###to add new support for FEAT00127,add by lizhi########
                                         [
                                          '-re', qr/Shutting down/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],

                                         ##############2011-08-02################################
                                         ### when config the last external interface to other, need input a gateway
                                         [
                                          '-re', qr/Default\s+gateway:$/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         #############################################
                                         ##############2011-12-30##########################
                                         ## add by shewn for BUG00188
                                         [
                                          '-re', qr/User.*status.*/,
                                          sub{
                                                 $self->latest_prompt($handle->match);
                                                 sleep ($interval);
                                             }
                                         ], 
                                         [
                                          '-re', qr/New\s+Password:$/,
                                          sub{
                                                 $self->latest_prompt($handle->match);
                                                 sleep ($interval);
                                             }
                                         ], 
                                         ##################################################
                                         
                               [
                                timeout =>
                                sub {
                                    $self->latest_prompt('[timeout]');
                                    $result[0]='nok';
                                  print "[wats:cmd wait prompt WG# or root\@Firebox:# timeout]\n";
                                  
                                }
                               ],
                               #'-re', qr'[#>:\?] $',
                               
                                        );
                        
                        #push the output into result can
                        if(defined($successfully_matching_string)){
                            $cmd_output="$cmd_output"."$before_match"."$successfully_matching_string"."$after_match";
                        }
                        $result[1]=$cmd_output;
                                                            
                        if($result[0] eq 'nok') {
                            print "[wats:will not try other command as one cmd timeout]\n";
                            last;
                            }

                        
                    }
        return @result; 
        
    }   

=pod
sub cmd{
        $self=shift;
        my $handle=$self->handle;
        my $rootuser=$self->root_user;
        my $timeout=150;
        my $cmd_output=$self->handle->match; #just a trick, let output 1st line show shell prompt
        my @result=('ok',$cmd_output);
        
        if(!defined($handle)){
                print "Handle is undef, can not execute any cmd. Return nok.\n";
            }
        #$handle->debug(3);
        
            #sleep corresponding time 
    my $cm = new ConfigurationManager;
        my $series;
        my $model=$self->model;
        if(lc($model) =~ m/xtm2/){
            $series = 'xtm2series';
        }
        elsif(lc($model) =~ m/xtm5/){
            $series = 'xtm5series';
        }
        elsif(lc($model) =~ m/xtm8/){
            $series = 'xtm8series';
            }
        elsif(lc($model) =~ m/xtm10/){
            $series = 'xtm10series';
            }
            
        my $interval = $cm->getCliCmdInterval($series);

        my $i = 0;
        
        while  (defined($_[$i])){
        my $cmd = $_[$i];
                if($cmd eq '?'){
                    #this is important,fix the bug of output not completed problem. as cmd ? does not need input \r or \n
                    $handle->send("$cmd");
                }
                else{   
                    $handle->send("$cmd\r");
              }
                my ($matched_pattern_position,
                     $error,
                     $successfully_matching_string,
                     $before_match,
                     $after_match)
                     =$handle->expect($timeout,
                                            [
                                           qr /Failed.*interface.*runtime.*information/,
                                          sub{
                                              sleep ($interval);
                                             $self->latest_prompt($handle->match);
                                            #   $handle->send("$cmd\r");
                                            #sleep ($interval);
                                            $i = $i -1;
                                            }
                                         ],
                                        [
                                          '-re', qr/WG.*#/,
                                          sub{
                                             #print "match.....\n";
                                             $self->latest_prompt($handle->match);
                                             #print "[WATS:sleep $interval sec]";
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         [
                                          #'-re', qr/$rootuser\@.*#/,                                         
                                          '-re', qr/\@.*#/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         #following block is to fix restore factory-default can not input 'yes' problem
                                         [
                                          '-re', qr/\(yes or no\)/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                                         ###add by gangKong to fix the problem of "Failed to get interface runtime information"
                                          
                                         
                                         #following block is to fix reboot cmd does not reflect 'ok' result problem
                                         [
                                          '-re', qr/Rebooting/,
                                          sub{
                                                $self->latest_prompt($handle->match);
                                              sleep ($interval);
                                              
                                            }
                                         ],
                               [
                                timeout =>
                                sub {
                                    $self->latest_prompt('[timeout]');
                                    $result[0]='nok';
                                  print "[wats:cmd wait prompt WG# or root\@Firebox:# timeout]\n";
                                  
                                }
                               ],
                               #'-re', qr'[#>:\?] $',
                               
                                        );
                        
                        #push the output into result can
                        if(defined($successfully_matching_string)){
                            $cmd_output="$cmd_output"."$before_match"."$successfully_matching_string"."$after_match";
                        }
                        $result[1]=$cmd_output;
                                                            
                        if($result[0] eq 'nok') {
                            print "[wats:will not try other command as one cmd timeout]\n";
                            last;
                            }

                        $i = $i+1;
                    }
        return @result; 
        
    }   
=cut

# /*!
#    @group Obtain Informaion Related 
# */


#  /*!
#    @function obtain_systeminfo
#    @return string - the system information with same format as CLI provide with cmd 'show sysinfo' from XTM CLI
#           If not success, then return 'nok'
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Get DUT system info.</pre>
#    <h2>example:</h2>
#    <pre>    my $wg = new XTM(HostIP=>'10.0.1.4');
#    <pre>    my $serialNo = $wg->cli->obtain_systeminfo();</pre>
#
# */
sub obtain_systeminfo{
    my $self=shift;
    my $result;
    my $infor;
    
    $result = $self->connect_cli; 
    my $if_new_handle = $self->if_new_handle;
    print "XtmCli:obtain_systeminfor: if_new_handle=$if_new_handle\n";
    
    if($result eq 'nok'){
        print "XtmCli:obtain_systeminfo: connect cli fail, return nok\n";
        return 'nok';
    }else{
        ($result, $infor)=$self->cmd("show sysinfo");
    }
    
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
        
    if($result eq 'ok'){
        return $infor;
    }else{
        return $result;
    }
        
}


# /*!
#    @function obtain_config_status
#    @param none
#    @return string - yes/no 
#    @discussion
#    <h2>Description: </h2>
#    <pre>   Return string of 'yes' if appliance configuration is in factory default state.
#            Return string of 'no' if appliance configuration is not in factory default state.</pre>
#    <h2>Example: </h2>
#    <pre> my $dut = new XTM(CLIMode=>'console', SNIP=>'x.x.x.x', SNPort=>'5003');
#          $dut->cli->obtain_config_status;
#          print $dut->cli->obtain_config_status;
#
# */
sub obtain_config_status{
    my $self=shift;
    my $result;
    my $infor;
    my $status;
            
    $result = $self->connect_cli; 
    my $if_new_handle = $self->if_new_handle;
    print "XtmCli:obtain_config_status: if_new_handle=$if_new_handle\n";
    
    if($result eq 'nok'){
        print "XtmCli:obtain_config_status: connect cli fail, return nok\n";
        return 'nok';
    }else{
        ($result, $infor)=$self->cmd("show factory-default");
    }
    
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    if($result eq 'ok'){
        @list = split(/\n/, $infor);
        foreach my $line (@list) {
            if ($line =~ m/factory default state:\s(.*)/) {
                chomp($status = "$1");
                $status =~ s/^\s+//;
                $status =~ s/\s+$//;
                last;
            }
        }
        return $status;
    }else{
        return $result;
    }
        
}

# /*!
#    @function obtain_serial
#    @param none 
#    @return string - the serial num of the box. 
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>   Obtain the serial num from box via CLI. . </pre>
#    <h2>example:</h2>
#    <pre> my $dut=new XTM(CLIMode=>'console', SNIP=>'x.x.x.x', SNPort=>'5003');  
#        <pre> $dut->cli->obtain_serial;    
#        <pre> print $dut->cli->obtain_serial;  </pre>
#
# */
sub obtain_serial{
    my $self=shift;
    my $infor;
    my @list;
    my @temp;
    
    $infor = $self->obtain_systeminfo;
    if($infor eq 'nok'){
            return 'nok';
        }
    else{
            @list= split(/\n/, $infor);
            foreach my $line(@list){
                if($line =~ m/serial number/){
                        @temp=split(/:/, $line);
                        $temp[1]=~ s/^\s+//;
                        $temp[1]=~ s/\s+$//;
                        last;
                            
                    }
                }
        
        }
        
    return $temp[1];
}


# /*!
#    @function obtain_model
#    @param none 
#    @return string - the platfor model of the box. 
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>   Obtain the model from box via CLI. . </pre>
#    <h2>example:</h2>
#    <pre> my $dut=new XTM(CLIMode=>'console', SNIP=>'x.x.x.x', SNPort=>'5003');  
#        <pre> $dut->cli->obtain_model; 
#        <pre> print $dut->cli->obtain_model;   </pre>
#
# */
sub obtain_model{
    my $self=shift;
    my $infor;
    my @list;
    my @temp;
    
    $infor = $self->obtain_systeminfo;
    if($infor eq 'nok'){
        return 'nok';
    } else {
        @list= split(/\n/, $infor);
        foreach my $line(@list){
            if($line =~ m/system model/){
                    @temp=split(/:/, $line);
                    $temp[1]=~ s/^\s+//;
                    $temp[1]=~ s/\s+$//;
                    last;
                        
                }
            }
        
        }
        
    return $temp[1];
}

# /*!
#    @function obtain_version_buildnum
#    @param none 
#    @return string - the version buildnum of the box. 
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>   Obtain version buildnum from box via CLI. . </pre>
#    <h2>example:</h2>
#    <pre> my $dut=new XTM(CLIMode=>'console', SNIP=>'x.x.x.x', SNPort=>'5003');  
#        <pre> $dut->cli->obtain_version_buildnum; 
#        <pre> print $dut->cli->obtain_version_buildnum;   </pre>
#
# */
sub obtain_version_buildnum{
    my $self=shift;
    my $infor;
    my @list;
    my @temp;
    
    $infor = $self->obtain_systeminfo;
    if($infor eq 'nok'){
        return 'nok';
    } else {
        @list= split(/\n/, $infor);
        foreach my $line(@list){
            if($line =~ m/version/){
                    @temp=split(/:/, $line);
                    $temp[1]=~ s/^\s+//;
                    $temp[1]=~ s/\s+$//;
                    last;
                        
                }
            }
        
        }
        
    return $temp[1];
}
    
# /*!
#    @group XTM CLI Application
# */    
    
# /*!
#    @function ping
#    @param $desip (the destination ip you want to ping,it can be an IPv4 or IPv6 address) 
#    @return an array \@result $result[0]:ok/nok. $result[1]: string which is possible reason of ping 'nok'
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>  This method is aimed to complete ping method accoring to cli.  </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->ping;  </pre>
#
# */
#sub ping{
#    my $this=shift();
#    my $desip=shift();
#    my $ret;
#    my $tmpret;
#    my $climode=$this->cli_mode();
#    my @result;
#    $ret=$this->connect_cli();
#    if($ret eq 'nok'){
#        if($climode eq 'console'){
#            $this->cli_mode("ssh");
#        }elsif($climode eq 'ssh'){
#            $this->cli_mode("console");
#        }
#      $tmpret=$this->connect_cli();
#      if($tmpret eq 'nok'){
#         $result[0]='nok';
#         $result[1]='cann\'t connect cli!!';
#         $this->cli_mode($climode);
#         return @result;    
#      } 
#    }
#    @result=$this->cmd("ping $desip -c 5");
#    if ($result[0] eq 'nok'){             
#            if($this->if_new_handle){
#            $this ->disconnect();
#            }
#            else{
#                $this->switch_to_cli;
#                }
#          $this->cli_mode($climode);
#          return @result;
#    }
#    if($result[1]=~/.*icmp_seq.*ttl.*time.*/i){
#        $result[0]='ok';
#        if($this->if_new_handle){
#            $this ->disconnect();
#        }
#        else{
#            $this->switch_to_cli;
#            }
#        $this->cli_mode($climode);
#        return @result;
#    }
#    $result[0]='nok';
#    $result[1]='ping is nok!';
#    if($this->if_new_handle){
#        $this ->disconnect();
#    }
#    else{
#        $this->switch_to_cli;
#        }
#    $this->cli_mode($climode);
#    return @result;
#}

sub ping{
       my $this=shift();
       my $desip=shift();
       my $ret;
       my $tmpret;
       my $climode=$this->cli_mode();
       my @result;
       $ret=$this->connect_cli();
       if($ret eq 'nok'){
           if($climode eq 'console'){
               $this->cli_mode("ssh");
           }elsif($climode eq 'ssh'){
               $this->cli_mode("console");
           }
         $tmpret=$this->connect_cli();
         if($tmpret eq 'nok'){
            $result[0]='nok';
            $result[1]='cann\'t connect cli!!';
            $this->cli_mode($climode);
            return @result;    
         } 
       }
        if ($desip !~ /\:/i) {
            print "go to IPv4\n";
           @result=$this->cmd("ping $desip -c 5");
        } 
        else {
            print "go to IPv6\n";
           @result=$this->cmd("ping -6 $desip -c 5");
        }    
       if ($result[0] eq 'nok'){             
               if($this->if_new_handle){
               $this ->disconnect();
               }
               else{
                   $this->switch_to_cli;
                   }
             $this->cli_mode($climode);
             return @result;
       }
       if($result[1]=~/.*icmp_seq.*ttl.*time.*/i){
           $result[0]='ok';
           if($this->if_new_handle){
               $this ->disconnect();
           }
           else{
               $this->switch_to_cli;
               }
           $this->cli_mode($climode);
           return @result;
       }
       $result[0]='nok';
       $result[1]='ping is nok!';
       if($this->if_new_handle){
           $this ->disconnect();
       }
       else{
           $this->switch_to_cli;
           }
       $this->cli_mode($climode);
       return @result;
}

# /*!
#    @function reset_factory_default
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2>
#    <pre>  This method is aimed to reset XTM box to factory default.   </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->reset_factory_default; </pre>
#
#
# */
sub reset_factory_default{
    
    my $self=shift;
    my $if_new_handle = $self->if_new_handle;
    my $result = $self->connect_cli;
    my $output;
    if ($result eq 'nok'){
        print "XtmCli:reset_factory_default: get connect_cli nok, return nok...\n";
        return 'nok';
    }
    my $latest_prompt= $self->latest_prompt;
    print "XtmCli: reset_factory_default :last prompt: $latest_prompt";
    ($result, $output)= $self->cmd("restore factory-default","yes");
    
    if($latest_prompt =~ /master/i){
        my $model=$self->model;
        if($model =~ m/[XxTtMm]2050/){
            $series = 'xtm20series';
        }
        elsif($model =~ m/[XxTtMm]2/){
            $series = 'xtm2series';
        }
        elsif($model =~ m/[XxTtMm]5/){
            $series = 'xtm5series';
        }
        elsif($model =~ m/[XxTtMm]3/){
                $series = 'xtm5series';
        }
        elsif($model =~ m/[XxTtMm]8/){
            $series = 'xtm8series';
        }
        elsif($model =~ m/[XxTtMm]10/){
            $series = 'xtm10series';
        }
    
        my $sleep_time = $config->getRebootWaitTime($series);
        print "XTMcli:reset_factory_default,box is in cluster mode,sleep $sleep_time seconds for XTM booting up before return ...\n";
        sleep ($sleep_time);        
    }
    if($if_new_handle){
        $self->disconnect;
    }
    else{
        $self->switch_to_cli;
        }
    return $result;
}
 

# /*!
#    @function import_whole_config 
#    @param $configfile     The config file which need to import
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Import a whole configure file to XTM box via cli without do any update for the config file before import</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $mycli->import_whole_config("/tmp/template.xml");</pre>
# */
sub import_whole_config{
  my ($this,$config_file) = @_;
  my $fileserver ;
    my @interface_result = `ifconfig | grep "inet addr" 2>&1`;
    
    my $manage_ip_address = $this->host_ip;
    foreach my $line (@interface_result) {
       chomp($line);
       if ($line =~ m/:(.*)Bcast/) {
                $_ = $1;
                s/\s+$//;
                my $string = $_;
                my ($eth1_ip) = $string =~ /(\d*\.\d*\.\d*\.)/g;
                print $eth1_ip."\n";
                if ($manage_ip_address =~ /$eth1_ip/g) {
                    $fileserver = $string;
                    last;
                }
                
        }
    }
    
    if ($fileserver !~ m/[\d\.]+/) {
        print "get controlserver's ip address failed!\n";
        return 'nok';
    }
    
    my $current_mode = $this->cli_mode;
    $this->cli_mode('console');
    $this->connect_cli;
    my $if_new_handle = $this->if_new_handle;
    my ($result) = $this->cmd("import config from ftp://automation:password\@$fileserver/$config_file");
    
    if($if_new_handle){
        $self->disconnect;
    } else{
        $self->switch_to_cli;
    }    
    $this->cli_mode("$current_mode");
    sleep(1);
    return $result;
}



# /*!
#    @function export_whole_config 
#    @param $configfile     The output whole config file which export from export
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Export whole configure file from XTM box into a file. 
#                   The operation will force change climode to console, after that the climode will change back </pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $mycli->export_whole_config("/tmp/output.xml");</pre>
# */
sub export_whole_config{
  my ($this,$config_file) = @_;
  my $fileserver ;
    my @interface_result = `ifconfig | grep "inet addr" 2>&1`;
    
    my $manage_ip_address = $this->host_ip;
    foreach my $line (@interface_result) {
       chomp($line);
       if ($line =~ m/.*inet addr:(.*)Bcast.*/) {
                $_ = $1;
                s/\s+$//;
                my $string = $_;
                my ($eth1_ip) = $string =~ /(\d*\.\d*\.\d*\.)/g;
                print $eth1_ip."\n";
                if ($manage_ip_address =~ /$eth1_ip/g) {
                    $fileserver = $string;
                    last;
                }
                
        }
    }
    
    my $current_mode = $this->cli_mode;
    $this->cli_mode('console');
    $this->connect_cli;
    my $if_new_handle = $this->if_new_handle;
    my ($result) = $this->cmd("export config to ftp://automation:password\@$fileserver/$config_file");
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    $this->cli_mode("$current_mode");
        
  sleep(1);
  return $result;
}

# 
#    @function configDut_bovpn
#    @param none 
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2>            
#    <pre>  This method is aimed to reset XTM box to factory default and then config DUT as bovpn static mode. </pre>
#    <h2>Example:</h2>
#    <pre> my $cli1= new XtmCli('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#        <pre> my $result=$cli1->configDut_bovpn;   </pre>
#
#   this function has expired, please use 'configBovpn'
# 
sub configDut_bovpn{    
        my $self=shift; 
        #$default_gw_ip,$dut_ctr_ip,$dut_eth2_ip,
        my ( $dut_sn_ip,$dut_sn_port,$gw_name,$tunnel_name,$dut_local_gw_ip,$dut_remote_gw_ip,$dut_remote_gw_id,$dut_local_subnet,$dut_remote_subnet,$dut_local_if_alias,$mode,$trf_index,$authentication,$encrytion,$sa_life,$encry_length,$key_group,$key_string,$proposal_name,$phase2_pfs ) = @_;
        print "configDut_bovpn has obsoleted, please use configBovpn function.\n";
        my $result = $self->connect_cli;
        if ($result eq 'nok'){
            print "XtmCli:configDut_bovpn: get connect_cli nok, return nok...\n";
            return 'nok';
        }
        my $if_new_handle = $self->if_new_handle;
        my @cli_cmd=(
        "config",
        "policy",
        "no bovpn-tunnel $tunnel_name",
        "no bovpn-gateway $gw_name",
        "bovpn-gateway $gw_name",
        "endpoint $dut_remote_gw_ip $dut_remote_gw_id $dut_local_gw_ip $dut_local_if_alias pre-shared $key_string",
        "no phase1 nat-traversal enable",
        "phase1 mode $mode",
        "phase1 transform $trf_index $authentication $encrytion $sa_life encryp-key-length $encry_length  $key_group",
        "exit",
        "bovpn-tunnel $tunnel_name",
        "gateway $gw_name network-ip $dut_local_subnet network-ip $dut_remote_subnet",
        "phase2 pfs enable $phase2_pfs",
        "phase2 proposal $proposal_name",
        "apply",
        "exit",
        "rule BOVPN-Allow.out",
        "move 1",
        "exit",
        "rule BOVPN-Allow.in",
        "move 1",
        "apply"
        
    );
        
        my @result_cmd= $self->cmd(@cli_cmd);
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return @result_cmd;
}


# /*!
#    @function add_policy 
#    @param $nickname the policy nickname, like "my_dns_policy_1"
#    @param $policy_type the policy-type, like "DNS-proxy","TCP-UDP-proxy"
#    @param $from the policy direction, like "alias Any-Trusted"
#    @param $to the policy direction, like "alias Any-External"
#    @param [optional]$position the policy position,the default value is "1"
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Add a policy to dutbox through cli.</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $mycli->add_policy("my_policy","DNS-policy","alias Any-Trusted","alias Any-External",1);</pre>
# */
sub add_policy{
    my $this = shift;

    my $nickname = shift;
    my $policy_type = shift;
    my $from = shift;
    my $to = shift;
    my $position = shift;
    my $second;

    $position or $position = 1;
    my @result;

    $this->del_policy($nickname);

    #if user do not specific the position, use default
    $result[0] = $this->connect_cli;
    my $if_new_handle = $this->if_new_handle;
    if ($result[0] eq 'nok'){
            print "XtmCli:add_policy: connect_cli nok, return nok\n";
            return 'nok';
    }
    
    $this->cmd("config",
                         "policy",
                         "rule $nickname"
                         );
    @result = $this->cmd("policy-type $policy_type from $from to $to");
    if(($result[0] eq 'nok')||(lc($result[1]) =~ m/error/)||(lc($result[1]) =~ m/invalid/)){
        #command not get good feedback on cli
        $result[0] = 'nok'; 
        $second = 0;
    }
    else{
        $this->cmd( "move $position","apply");
        $second = 5;
    }
            
        print "XtmCli:add_policy: sleep $second sec to make policy take into effective..\n" 
        unless ($second eq 0);
    print "XtmCli:add_policy: seems operation get fail,its probably your input informaiton not valid for this policy...\n"
    unless ($result[0] eq 'ok');
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    sleep $second;
    return $result[0];
}



# /*!
#    @function del_policy 
#    @param $nickname the policy nickname, like "my_dns_policy"
#    @return ok|nok
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Delete a policy from XTM box via CLI.</pre>
#    <h2>Example:</h2>
#    <pre>      my $mycli = new XtmCli(HostIP=>'1.1.1.1');
#       <pre>       $mycli->del_policy("my_dns_policy");                            
#       <pre>       #following use CLI via console
#       <pre>       $mycli->cli_mode('console');
#       <pre>       $mycli->sn_ip('2.2.2.2');
#       <pre>       $mycli->sn_port('5001');
#       <pre>       $mycli->del_policy("my_ftp_policy");
#       </pre>
# */

sub del_policy{
    my $this = shift;
    my $nickname = shift;
        my @result;
        my $second;
        
        print "XtmCli:del_policy: trying to delete policy $nickname\n";
        $result[0] = $this->connect_cli;
        my $if_new_handle = $this->if_new_handle;
        if($result[0] eq 'nok'){
            print "XtmCli:del_policy: connect_cli get nok, return nok...\n";    
            return 'nok';
        }
        
    $this->cmd("config","policy");
    @result = $this->cmd("no rule $nickname");
    if(($result[0] eq 'nok')||($result[1] =~ m/Error/)){
        #meet '%Error: Rule 'pop3_proxy' not found' situation
        #We Do NOT threat this situation as 'nok' because if there is no this rule then it is just what we want when call this api.
        $second = 0;
    }
    else{
        $this->cmd("apply");
        $second = 5;
        }

    
    
    print "XtmCli:del_policy: sleep $second seconds to make sure action take into effective..\n"
    unless ($second eq 0);
    print "XtmCli:del_policy: seems operation get fail...\n"
    unless ($result[0] eq 'ok');
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    sleep $second;
    return $result[0];
}


# /*!
#    @function enable_policy 
#    @param $nickname the policy nickname, like "my_dns_policy_1"
#    @return ok|nok
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Enable a policy to dutbox through cli.</pre>
#    <h2>Example:</h2>
#    <pre>      my $mycli = new XtmCli(HostIP=>'1.1.1.1');
#        <pre>      $mycli->enable_policy("my_dns_policy");                         
#        <pre>      #following use CLI via console
#        <pre>      $mycli->cli_mode('console');
#        <pre>      $mycli->sn_ip('2.2.2.2');
#        <pre>      $mycli->sn_port('5001');
#        <pre>      $mycli->enable_policy("my_ftp_policy");
#        </pre>
# */

sub enable_policy{
    
    my $this = shift;
    my $nickname = shift;
    my @result;
    my $second;

        print "XtmCli:enable_policy: trying to enable policy $nickname\n";
        $result[0] = $this->connect_cli;
        if($result[0] eq 'nok'){
            print "XtmCli:enable_policy: connect_cli get nok, return nok...\n"; 
            return 'nok';
        }
        my $if_new_handle = $this->if_new_handle;
    $this->cmd("config","policy","rule $nickname");
    @result = $this->cmd("enable");
    if(($result[0] eq 'nok')||(lc($result[1]) =~ m/invalid/)||(lc($result[1]) =~ m/error/)){
        $result[0] = 'nok';
        $second = 0;
    }
    else{
        $this->cmd("apply");    
        $second = 5;
    }
    
    
    
    print "XtmCli:enable_policy: sleep $second seconds to make sure action take into effective..\n"
    unless ($result[0] eq 'nok');
    print "XtmCli:enable_policy: operation get fail,its probably the policy does not exist..\n"
    unless ($result[0] eq 'ok');
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    sleep $second;
    return $result[0];

}


# /*!
#    @function disable_policy 
#    @param $nickname the policy nickname, like "my_dns_policy_1"
#    @return ok|nok
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Disable a policy to dutbox through cli.</pre>
#    <h2>Example:</h2>
#     <pre>     my $mycli = new XtmCli(HostIP=>'1.1.1.1');
#           <pre>       $mycli->disable_policy("my_dns_policy");                            
#           <pre>       #following use CLI via console
#           <pre>       $mycli->cli_mode('console');
#           <pre>       $mycli->sn_ip('2.2.2.2');
#           <pre>       $mycli->sn_port('5001');
#           <pre>       $mycli->disable_policy("my_ftp_policy");
#        </pre>
# */

sub disable_policy{
    my $this = shift;
    my $nickname = shift;
    my @result;
    my $second;
    
    print "XtmCli:disable_policy: trying to disable policy $nickname\n";
    $result[0] = $this->connect_cli;
    if($result[0] eq 'nok'){
        print "XtmCli:disable_policy: connect_cli get nok, return nok...\n";    
        return 'nok';
    }
    my $if_new_handle = $this->if_new_handle;
    
    if ($nickname =~ m/\w+\s+\w+/) {
        @result = $this->cmd("config","policy","rule \"$nickname\"","no enable");
    } else {
        @result = $this->cmd("config","policy","rule $nickname","no enable");
    }
    
    if(($result[0] eq 'nok')||(lc($result[1]) =~ m/invalid/) ||(lc($result[1]) =~ m/error/)){
        $result[0]='nok';
        $second = 0;
    }
    else{
        $this->cmd("apply");
        $second=    5;
    }
    
    
    
    print "XtmCli:disable_policy: sleep $second seconds to let action take into effective..\n"  
    unless ($result[0] eq 'nok');
    print "XtmCli:disable_policy: seems operation get fail,its probabely there is no this policy...\n"
    unless ($result[0] eq 'ok');
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    sleep $second;
    return $result[0];
}



# /*!
#    @function del_one_to_one_nat 
#    @param Global 1-1 NAT ID. If id not specified, 1 will be used as default
#    @return ok|nok
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Delete all 1-to-1-nat from XTM box via CLI.</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli(HostIP=>"10.1.1.1",SNIP=>'x.x.x.x', SNPort=>'5011');
#    <pre>    my $mycli->del_one_to_one_nat();</pre>
# */

sub del_one_to_one_nat {
    my $this = shift;   
    my $id;
    my @result;
    my $second;
    
    if(@_){
        $id = shift;
    }
    else{
        $id = 1;
    }
    
    print "XtmCli:del_one_to_one_nat: trying to delete 1-1 NAT with globle id=$id\n";
  $result[0] = $this->connect_cli;
  if($result[0] eq 'nok'){
        print "XtmCli:del_one_to_one_nat: connect_cli get nok, return nok...\n";    
        return 'nok';
    }
    my $if_new_handle = $this->if_new_handle;
  $this->cmd("config","policy");
  @result =  $this->cmd("no one-to-one-nat $id");
  if(($result[0] eq 'nok')||(lc($result[1]) =~ m/invalid/)||(lc($result[1]) =~ m/error/)){
    #not treat this situation as nok because user need to move this out of box
    $second = 0;
  }
  else{
    $this->cmd("apply");
    $second = 5;    
  }
  
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    print "XtmCli:del_one_to_one_nat: sleep $second seconds to make sure action take into effective..\n" unless ($second eq 0);
  sleep $second;
  return $result[0];

  
}

# /*!
#    @function add_host_one_to_one_nat 
#    @param $NatAddress Address for the NAT address
#    @param $RealAddress Address for the real address
#    @param $interface The interface name(notice: it is interface name eg. 'External', instead of interface num) for the one-to-one NAT
#    @return ok|nok
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    Add host 1-1 NAT.</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli(HostIP=>"10.1.1.1",SNIP=>'x.x.x.x', SNPort=>'5011');
#    <pre>    my $mycli->add_host_one_to_one_nat(NatAddress, RealAddress, interface);</pre>
# */

sub add_host_one_to_one_nat {
    my $this = shift;   
    my $NatAddress = shift;
    my $RealAddress = shift;
    my $interface = shift;
    my @result;
    my $second;
    
    print "XtmCli:add_host_one_to_one_nat: try to add 1-1 NAT with NatAddress=$NatAddress, RealAddress=$RealAddress, interface=$interface\n";
    
    $result[0] = $this->connect_cli;
  if($result[0] eq 'nok'){
        print "XtmCli:add_host_one_to_one_nat: connect_cli get nok, return nok...\n";   
        return 'nok';
    }
    my $if_new_handle = $this->if_new_handle;
    $this->cmd("config","policy");
    @result = $this->cmd("one-to-one-nat host $NatAddress $RealAddress $interface");
    if(($result[0] eq 'nok')||(lc($result[1]) =~ m/error/)||(lc($result[1]) =~ m/invalid/)){
            $result[0]='nok';
            $second = 0;
    }
    else{
            $this->cmd("apply");
            $second = 5;
    }
    
  if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    print "XtmCli:add_host_one_to_one_nat: sleep $second seconds to make sure action take into effective..\n"
    unless ($result[0] eq 'nok');
    print "XtmCli:add_host_one_to_one_nat:seems add one-to-one-nat host NOT ok\n"
    unless ($result[0] eq 'ok');
    
  sleep $second;
  return $result[0];

}


#   
#    @function ConfigBovpn_DD
#    @param ($dut1_local_domain_name,$dut1_remote_domain_name,$dut1_local_if_alias,$dut1_local_subnet,$dut1_remote_subnet,$gw_name,$tunnel_name,$key_string,$dns_ip,$mode,$trf_index,$authentication,$encrytion,$sa_life,$key_group,$phase2_pfs,$proposal_name)
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Configure current DUT as dynmic bovpn.   </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my \@result = $dut->ConfigBovpn_DD(...);  
#        <pre> if($result eq 'nok'){    
#    <pre>    print "config bovpn failed";  
#    <pre> }    </pre>
# 
#   this function has expired, please use 'configBovpn'
#
sub ConfigBovpn_DD{
    my $self = shift; 
    my ($dut_local_domain_name,$dut_remote_domain_name,$dut_local_if_alias,$dut_local_subnet,$dut_remote_subnet,$gw_name,$tunnel_name,$key_string,$dns_ip,$mode,$trf_index,$authentication,$encrytion,$sa_life,$key_group,$phase2_pfs,$proposal_name) = @_;   
    print "ConfigBovpn_DD has expired, please use configBovpn function.\n";
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    print "Function ConfigBovpn_DD is obsoleted, please use 'configBovpn' to config bovpn.\n";
    ###########################2011-07-28 modify by lizhi##################
    my $yes = '';#if version >= 11.5,need yes when config doamin_name
    my $sys_info = $self->obtain_systeminfo();
    
    if ($sys_info ne 'nok') {
        if ($sys_info =~ m/version\s+:\s+11\.4/) {
            #$yes = 'yes';
        } else {
              $yes = $params{resolve} ? $params{resolve}: "yes"; 
        }
    }
    ##################################################################
    my @result_cmd = $self->cmd(
            "config",
            "ip dns server $dns_ip",
            "no ip dns server 172.23.0.5",
            "interface fastEthernet 0",
            "name $dut_local_if_alias",
            "exit",
            "policy",            
            "no bovpn-tunnel $tunnel_name",
            "no bovpn-gateway $gw_name",
            "bovpn-gateway $gw_name",
            "endpoint dynamic by-domain domain-name $dut_remote_domain_name.wats $yes by-domain domain-name $dut_local_domain_name.wats $dut_local_if_alias pre-shared $key_string",
            "no phase1 nat-traversal enable",
            "phase1 mode $mode",
            "phase1 transform $trf_index $authentication $encrytion $sa_life  $key_group",
            "apply",
            "exit", 
            "bovpn-tunnel $tunnel_name",
            "gateway $gw_name network-ip $dut_local_subnet network-ip $dut_remote_subnet",
            "phase2 pfs enable $phase2_pfs",
            "phase2 proposal $proposal_name",
            "apply",
            "exit",
            "rule BOVPN-Allow.out",
            "move 1",
            "exit",
            "rule BOVPN-Allow.in",
            "move 1",
            "apply"
            );
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    return @result_cmd;
}   

# /*!   
#    @function ClearConfigBovpn_DD
#    @param ($gw_name,$tunnel_name,$dns_ip)
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  Clear current DUT dynmic bovpn configure.  </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my \@result = $dut1->cli->ClearConfigBovpn_DD($gw_name,$tunnel_name,$dns_ip);  
#        <pre> if($result eq 'nok'){    
#    <pre>    print "clear config bovpn failed";  
#    <pre> }    </pre>
# */
sub ClearConfigBovpn_DD(){
    my $self = shift; 
    my ( $gw_name,$tunnel_name,$dns_ip ) = @_;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    my @clear_dns;
    if( scalar(@_) > 2 ){
        push(@clear_dns,"no ip dns server $dns_ip");
    }else{
        push(@clear_dns," ");
    }
    my @result_cmd = $self->cmd(
            "config",
            @clear_dns,
            "policy",
            "no rule Ping",
            "no bovpn-tunnel $tunnel_name",
            "no bovpn-gateway $gw_name",
            "apply"
            );
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    return @result_cmd;
}


# /*!   
#    @function EnableDhcpServer
#    @param 
#    @return $result  $result:ok/nok. 
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  Enable DHCP server on special interface </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my $result = $dut1->cli->EnableDhcpServer(
#                                                                                                           eth => 'eth2', 
#                                                                                                           add-pool-mount => 10, 
#                                                                                                           lease => 16, 
#                                                                                                           dns-server => '2.2.2.2',
#                                                                                                           wins => '3.3.3.3',
#                                                     domain => 'foo.com',
#                                                                                                           reservation => {
#                                                                                                                                            'res1' => ['01:23:45:67:89:ab','192.168.11.23'],
#                                                                                                                                           }
#                                                                                                               );  
#
#
#        <pre> if($result eq 'nok'){    
#    <pre>    print "enable dhcp server fail";  
#    <pre> }    </pre>
# */
sub EnableDhcpServer {
    my $self = shift;
    my %arg_hash = @_;
    my $lease = 8;
    my $config_dns;
    my $config_wins;
    my $config_domain;
    my $config_add_pool;
    my $config_reservation;
    my $interface;
    exists $arg_hash{'eth'} or die LogInfo("invalid parameter: please define eth");
  $arg_hash{'eth'} =~ m/^eth(\d+)/i or die LogInfo("invalid parameter eth value, please define eth0~eth11");
  exists $arg_hash{'add-pool-mount'} or die LogInfo("invalid parameter: please define add-pool-mount");
    $arg_hash{'add-pool-mount'} <= 50 or die LogInfo("add-pool-mount value should between 1~50");
    $interface = $1;
    if(exists $arg_hash{'lease'}) {
        $lease = $arg_hash{'lease'};
        }
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    my @result_cmd = $self->cmd("show interface $interface");
    my $start_add;
    my $end_add = 200 + int($arg_hash{'add-pool-mount'});
    if($result_cmd[0] eq 'ok' and $result_cmd[1] !~ m/.*\^.*/) {
        if ($result_cmd[1] =~ m/(interface type\s+:\s+trusted|interface type\s+:\s+optional).*/i) {
                $result_cmd[1] =~ m/ip address\s+:\s+(\d+\.\d+\.\d+)\.(\d+)/i; 
                $start_add = $1.'.'.'200';
                $end_add = $1.'.'.$end_add;
                
            } else {
                LogInfo("please set interface $interface as trusted or optional type first");
                if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
                return 'nok';
            }
        } else {
            LogInfo("enable DHCP server on interface $interface fail due to $result_cmd[1]");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return 'nok';
            }
    $config_add_pool = "dhcp server start-addr $start_add $end_add $lease";
    $self->switch_to_cli();
    my $cmd_output = join '', map{my @result = $self->cmd($_);$result[1]} ("config","interface fastEthernet $interface",'no dhcp enable',$config_add_pool);
    if($cmd_output =~ m/$error_tips/i){
        LogInfo("enable DHCP fail due to $cmd_output");
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return 'nok';
        }
    if(exists $arg_hash{'dns-server'}) {
        $config_dns = "dhcp server dns-server $arg_hash{'dns-server'}";
        my @result = $self->cmd($config_dns);
        if($result[1] =~ m/$error_tips/i) {
            LogInfo("set dns-server fail due to $result[1]");
            $self->cmd("no dhcp enable");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return 'nok';
            }
        }
    if(exists $arg_hash{'wins'}) {
        $config_wins = "dhcp server wins $arg_hash{'wins'}";
        my @result = $self->cmd($config_wins);
        if($result[1] =~ m/$error_tips/i) {
            LogInfo("set win server fail due to $result[1]");
            $self->cmd("no dhcp enable");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return 'nok';
            }
        }
    if(exists $arg_hash{'domain'}) {
        $config_domain = "dhcp server domain $arg_hash{'domain'}";
        my @result = $self->cmd($config_domain);
        if($result[1] =~ m/$error_tips/i) {
            LogInfo("set domain fail due to $result[1]");
            $self->cmd("no dhcp enable");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return 'nok';
            }
        }
    if(exists $arg_hash{'reservation'}) {
        my $res_hash_ref = $arg_hash{'reservation'};
        my $res_output = join '' , map{
            my $res_pair_ref = $res_hash_ref->{$_};
            my $res_mac = $res_pair_ref->[0];
            my $res_ip = $res_pair_ref->[1];
            my @cmd_result = $self->cmd("dhcp server reservation $_ $res_mac $res_ip");
            $cmd_result[1];
            }keys %$res_hash_ref;
        
        if($res_output =~ m/$error_tips/i) {
            LogInfo("set reservation fail due to $res_output");
            $self->cmd("no dhcp enable");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return 'nok';
            }
        }
  LogInfo("Enable DHCP server success");
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    sleep(1);
    return 'ok';    
    }
    
    
sub dropin_mode{
    my $self = shift;
    my $dns=   shift;
    my $ip = shift;
    my $netmask = shift();
    my $gw = shift;
    
    my @cmd_result;
    
    my $save_mode = $self->mode;
    $self->mode("console");
    $self->connect_cli();
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd("show network-mode");
    if($cmd_result[0] eq 'nok'){
        $self->disconnect();
        $self->mode($save_mode);
        return 'nok';
    }
    
    if($cmd_result[1]  =~ /drop-in/){
        @array = $self->cmd("configure","ip dns server $dns","network-mode drop-in $ip $netmask $gw","exit");
    }else{
        @array = $self->cmd("configure","ip dns server $dns","network-mode drop-in $ip $netmask $gw","yes","exit");
    }
    if($array[0] eq 'nok'){
        $self->mode($save_mode);
      if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
       return 'nok';
    }
    
    
    my $times = 0;
    while(1){
        @cmd_result = $self->ping($gw);
        if($cmd_result[0] eq 'ok'){
            last;
        }   
        $times = $times +1;
        
        if($times >=15){
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            $self->mode($save_mode);
            return 'nok';
        }
        sleep 10;
    }
    
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    $self->mode($save_mode);
    return 'ok';
    }
    
sub bridge_mode{
    my $self = shift;
    my $dns=    shift;
    my $ip = shift;
    my $netmask = shift();
    my $gw = shift;
    
    my @cmd_result;
    my $save_mode;
    
    $save_mode = $self->mode;
    $self->mode("console");
    $self->connect_cli();
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd("show network-mode");
    if($cmd_result[0] eq 'nok'){
      if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        $self->mode($save_mode);
        return 'nok';
    }
    
    if($cmd_result[1]  =~ /bridge/){
        @array = $self->cmd("configure","ip dns server $dns","network-mode bridge $ip $netmask $gw","exit");
    }else{
        @array = $self->cmd("configure","ip dns server $dns","network-mode bridge $ip $netmask $gw","yes","exit");
    }
    if($array[0] eq 'nok'){
      if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        $self->mode($save_mode);
        return 'nok';
    }
    
    
    my $times = 0;
    while(1){
        @cmd_result = $self->ping($gw);
        if($cmd_result[0] eq 'ok'){
            last;
        }   
        $times = $times +1;
        
        if($times >=15){
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            $self->mode($save_mode);
            return 'nok';
        }
        sleep 10;
    }
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        $self->mode($save_mode);
        return 'ok';
    }
    
# /*!   
#    @function cluster_mode
#    @param 
#    @return $result  $result:ok/nok. 
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  change cluster mode </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my $result = $dut1->cli->EnableDhcpServer(
#                                                                                                           eth => 'eth2', 
#                                                                                                           add-pool-mount => 10, 
#                                                                                                           lease => 16, 
#                                                                                                           dns-server => '2.2.2.2',
#                                                                                                           wins => '3.3.3.3',
#                                                     domain => 'foo.com',
#                                                                                                           reservation => {
#                                                                                                                                            'res1' => ['01:23:45:67:89:ab','192.168.11.23'],
#                                                                                                                                           }
#                                                                                                               );  
#
#
#        <pre> if($result eq 'nok'){    
#    <pre>    print "enable dhcp server fail";  
#    <pre> }    </pre>
# */
sub cluster_mode {
    my $self = shift;
    my $cluster_mode = shift;
    my $cluster_current_mode;
    my @cmd_result;
    my $result;
    my $reboot_flag = 0;
    my $cluster_flag_after_reboot = 0;
    $cluster_mode =~ m/(active-active|active-passive)/ or die LogInfo("invalid parameter: set active-active|active-passive");
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    $self->cmd('config');
    my $prompt = $self->latest_prompt;
    if ($prompt eq 'WG(config)<master>#') {
        @cmd_result = $self->cmd("show cluster");
        if ($cmd_result[1] =~ m/Mode\s+:\s+(.*)/) {
        $cluster_current_mode = lc($1);
        }       
    }   
    my $cluster_mode_command = "cluster mode $cluster_mode";
    @cmd_result = $self->cmd($cluster_mode_command);
        if($cmd_result[1] =~ m/$error_tips/i) {
            LogInfo("set cluster mode fail due to $cmd_result[1]");
            return 'nok';
            }
    
    #return;
    if ($prompt eq 'WG(config)<master>#') {
        
        if ($cluster_current_mode =~ qr/$cluster_mode/) {
            #change cluster mode as the same mode before, no need to reboot appliance
            LogInfo("change cluster mode as the same mode before, no need to reboot appliance,change mode success");
            return 'ok'; 
            } else {
        #cluster has been create, need to wait for reboot after change mode
        LogInfo("change cluster mode when cluster enable, wait for reboot...");
        my $exp = $self->handle;
        #
        my $timeout = 300;
        $exp->expect($timeout,

                         [
                          qr'Restarting system',
                          sub{
                            my $fh = shift;
                            LogInfo("appliance is rebooting...,wait for 60 seconds");
                            sleep (160);
                            $reboot_flag = 1;
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  my $adminuser = $self->admin_user();
                  $fh->send("$adminuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  my $adminpasswd = $self->admin_passwd();
                  $fh->send("$adminpasswd\n");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               [
                qr /Login incorrect/,
                sub {
                    print "[get login incorrect...]";
                    $result="nok";
                    $exp->hard_close();
                 
                }
               ],
               [
                qr/WG\(.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                #qr/$rootuser\@.*#/,
                qr/\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("cli\r");
                  exp_continue;
                }
               ],
               [
                            qr/\%Error: Login failed. \'admin\' is currently logged in/,
                            sub {
                                
                                print "[WATS: try to force logoff admin...]\n";
                                $self->{Handle} = $exp;
                                my $ret_s = $self->switch_to_shell;
                                if($ret_s eq 'ok'){
                                    $self->cmd("wgcmd action \/authentication\/logoff type=6"); 
                                }
                                else{
                                    print "[WATS:can not go into shell..]\n";   
                                    $result = 'nok';
                                        $exp->hard_close();
                                        print "[admin is currently used by another session,but can not logoff]\n";
                                    
                                }
                                    $exp->send("\r");
                                    exp_continue;
                                    
                            }
                         ],
               [
                          qr /WG#|WG<non-master>#/,
                          sub{
                            my $fh = shift;
                            LogInfo("wait 5 seconds for appliance cluster mode change take effect...");
                            sleep (5);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               
               #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
                             '-re', qr/(WG<master>#|WG<non-master>#)/, sub { $cluster_flag_after_reboot = 1;}
              );
        
        if($cluster_flag_after_reboot == 1 and $reboot_flag ==1) {
            LogInfo("change cluster mode success");
            $result = 'ok';
        } else {
            LogInfo("change cluster mode fail");
            $result = 'nok';
            }
        }
        } elsif ($prompt eq 'WG(config)#') {
        #cluster hasn't been create, no need to wait for reboot after change mode
        LogInfo("change cluster mode when cluster disable, no need to wait for reboot");
        $result = 'ok';
        } else {
        LogInfo("didn't match any prompt, go to wrong branch");
        $result = 'nok';
        }
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    return $result;
    }
# /*!   
#    @function cluster_enable
#    @param 
#    @return $result  $result:ok/nok. 
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>  enable cluster </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my $result = $dut1->cli->EnableDhcpServer(
#                                                                                                           eth => 'eth2', 
#                                                                                                           add-pool-mount => 10, 
#                                                                                                           lease => 16, 
#                                                                                                           dns-server => '2.2.2.2',
#                                                                                                           wins => '3.3.3.3',
#                                                     domain => 'foo.com',
#                                                                                                           reservation => {
#                                                                                                                                            'res1' => ['01:23:45:67:89:ab','192.168.11.23'],
#                                                                                                                                           }
#                                                                                                               );  
#
#
#        <pre> if($result eq 'nok'){    
#    <pre>    print "enable dhcp server fail";  
#    <pre> }    </pre>
# */
sub cluster_enable {
    my $self = shift;
    my @cmd_result;
    my $result = 'nok';
    $self->mode('console');
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd('config');
    my $prompt = $self->latest_prompt;
    if($cmd_result[1] !~ m/$error_tips/i) {
        #enter config mode
        if ($prompt eq 'WG(config)<master>#') {
        #already under cluster master mode
        @cmd_result = $self->cmd("cluster enable");
        if($cmd_result[1] =~ m/$error_tips/i) {LogInfo("can't enable cluster due to $cmd_result[1]");return $result;}
        LogInfo("under the mater config mode, no need to wait, enable cluster success");
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return 'ok';
        } elsif($prompt eq 'WG(config)#') {
        #under single configure mode,
        @cmd_result = $self->cmd("cluster enable");
        if($cmd_result[1] =~ m/$error_tips/i) {LogInfo("can't enable cluster due to $cmd_result[1]");return $result;}
        LogInfo("under the single config mode, wait for cluster create until appliance become master");
        my $exp = $self->handle;
        my $timeout = 300;
        $exp->send("exit\r");
        $exp->expect($timeout,
                             [
                          qr/WG#|WG<non-master>#/,
                          sub{
                            my $fh = shift;
                            LogInfo("wait 5 seconds for appliance cluster enable...");
                            sleep (5);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               
                         [
                eof =>
                sub {
                  return $result;
                }
               ],
               [
                timeout =>
                sub {
                                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                  return $result;
                }
               ],
               '-re', qr/WG<master>#/, sub { sleep 10; LogInfo("cluster enable success");}
              );
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    return 'ok';    
        } else {
            #under unknow mode
            LogInfo("enter unknow mode prompt is $prompt, can't enable cluster");
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return $result;
            }   
    } else {
        #can't enter config mode, maybe error or in <non-mater> mode
        LogInfo("can't enter configure mode dut to $result[1]");
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return $result;
        }
    
    
    }

sub cluster_disable {
    
    my $self = shift;
    my @cmd_result;
    my $result = 'nok';
    my $reboot_flag;
    my $cluster_flag_after_reboot;
    $self->mode('console');
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd('config');
    my $prompt = $self->latest_prompt;
    if($cmd_result[1] !~ m/$error_tips/i) {
        #enter config mode
        if ($prompt eq 'WG(config)<master>#') {
            #enter cluster master config mode , need wait for appliance reboot
        @cmd_result = $self->cmd("no cluster enable");
        if($cmd_result[1] =~ m/$error_tips/i) {LogInfo("can't disable cluster due to $cmd_result[1]");return $result;}
        LogInfo("under the cluster config mode, wait for reboot.");
        my $exp = $self->handle;
        #
        my $timeout = 300;
        $exp->expect($timeout,

                         [
                          qr'Restarting system',
                          sub{
                            my $fh = shift;
                            LogInfo("appliance is rebooting...,wait for 200 seconds");
                            sleep (200);
                            $reboot_flag = 1;
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               [
                qr/[Ll]ogin:/,
                sub {
                  my $fh = shift;
                  my $adminuser = $self->admin_user();
                  $fh->send("$adminuser\r");
                  exp_continue;
                }
               ],
               [
                qr/[Pp]assword:/,
                sub {
                  my $fh = shift;
                  my $adminpasswd = $self->admin_passwd();
                  $fh->send("$adminpasswd\n");
                  exp_continue;
                }
               ],
               [
                eof =>
                sub {
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ],
               [
                qr /Login incorrect/,
                sub {
                    print "[get login incorrect...]";
                    $result="nok";
                    $exp->hard_close();
                 
                }
               ],
               [
                qr/WG\(.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("exit\n");
                  exp_continue;
                }
               ],
               [
                #qr/$rootuser\@.*#/,
                qr/\@.*#/,
                sub {
                  my $fh = shift;
                  $fh->send("cli\r");
                  exp_continue;
                }
               ],
               [
                            qr/\%Error: Login failed. \'admin\' is currently logged in/,
                            sub {
                                
                                print "[WATS: try to force logoff admin...]\n";
                                $self->{Handle} = $exp;
                                my $ret_s = $self->switch_to_shell;
                                if($ret_s eq 'ok'){
                                    $self->cmd("wgcmd action \/authentication\/logoff type=6"); 
                                }
                                else{
                                    print "[WATS:can not go into shell..]\n";   
                                    $result = 'nok';
                                        $exp->hard_close();
                                        print "[admin is currently used by another session,but can not logoff]\n";
                                    
                                }
                                    $exp->send("\r");
                                    exp_continue;
                                    
                            }
                         ],
               [
                          qr /(WG<master>#|WG<non-master>#)/,
                          sub{
                            my $fh = shift;
                            LogInfo("wait 5 seconds for appliance cluster mode change take effect...");
                            sleep (5);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               
               #'-re',qr/WG#/, #' wait for shell prompt, then exit expect  #DEBUG MATCH
                             '-re', qr/WG#/, sub { $cluster_flag_after_reboot = 1;}
              );
        
        if($cluster_flag_after_reboot == 1 and $reboot_flag ==1) {
            LogInfo("dsiable cluster success");
            $result = 'ok';
        } else {
            LogInfo("change cluster mode fail");
            $result = 'nok';
            }
            if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
            return $result;
            } elsif($prompt eq 'WG(config)#') {
             #single config mode, no need to wait for reboot
             @cmd_result = $self->cmd("no cluster enable");
             if($cmd_result[1] =~ m/$error_tips/i) {LogInfo("can't disable cluster due to $cmd_result[1]");return $result;}
             LogInfo("enter single config mode, no need to wait for reboot");
             if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
             return 'ok';
            } else {
                #under unknow mode
                LogInfo("enter unknow mode prompt is $prompt, can't enable cluster");
                if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
                return $result;
                }
        } else {
        #can't enter config mode, maybe error or in <non-mater> mode
        LogInfo("can't enter configure mode dut to $result[1]");
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return $result;
        }
    }

sub _create_license {
    my $serial = shift;
    my $license = shift;
    my $license_file = "$tmp_dir/$serial".'.lic';
    open(FD,"+> $license_file");
    print FD $license;
    close(FD);
    return "$serial".'.lic';
    }

sub _local_ip {
    my $cmd = 'ifconfig eth1 | grep "inet addr" | awk \'{print $2}\' | awk -F : \'{print $2}\'' ;
    my $ip_addr = `$cmd`;
    $ip_addr =~ s/\n+//;
    return $ip_addr;
    }


sub cluster_add_member {
    my $self = shift;
    my %arg_hash = @_;
    my @arg_list = qw(serial_number primary_ip management_ip license import_way);
    map{
        unless (exists $arg_hash{$_}) {
        die LogInfo("set $_");
            }
        } @arg_list;
    #print "==========debug============",__FILE__,__LINE__,"\n";
    #print Dumper(\%arg_hash);
    #print "==========debug============","\n";
  my $license_file = _create_license($arg_hash{'serial_number'},$arg_hash{'license'});
    my $import_string;
    my $command;
    if($arg_hash{'import_way'} eq 'ftp') {
            $import_string = 'ftp://automation:password@'._local_ip().'/'.$license_file;    
            } elsif($arg_hash{'import_way'} eq 'tftp') {
            $import_string = 'tftp://automation:password@'._local_ip().'/'.$license_file;   
            } elsif($arg_hash{'import_way'} eq 'console') {
                unless ($self->cli_mode eq 'console'){die LogInfo("didn't connect box console, can't import license from console")}
            } else {
                die LogInfo("set import_way as ftp/tftp/console");      
                }       
        
    my $member_name = 'Member2';
    if (exists $arg_hash{'member_name'}) {
        $member_name = $arg_hash{'member_name'};
        } 
    $command = "cluster member add $member_name $arg_hash{serial_number} $arg_hash{primary_ip}/24 $arg_hash{management_ip}/24 from $import_string";
    my @cmd_result;
    my $result = 'nok';
    my $reboot_flag;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd('config',$command);
    my $prompt = $self->latest_prompt;
    if($cmd_result[1] !~ m/$error_tips/i) {
        LogInfo("Edit cluster member success");
        $result = 'ok';
        } else {
        #can't enter config mode, maybe error or in <non-mater> mode
        LogInfo("can't enter configure mode dut to $cmd_result[1]");
        
        }
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        eval{unlink("$tmp_dir/$license_file");};
        return $result;
    }

sub cluster_edit_member {
    my $self = shift;
    my %arg_hash = @_;
    my %arg_check = qw (member 1 management-ip 1 primary-ip 1 secondary-ip 1);
    my @commands_list;
    push(@commands_list,'config');
    #do arg check here
    foreach my $arg (keys %arg_hash) {
        unless (exists $arg_check{$arg}) {die LogInfo("Unknow ARG name $arg")}
        unless ($arg eq 'member') {
            my $command = "cluster member edit $arg_hash{member} $arg $arg_hash{$arg}/24";
            push(@commands_list, $command);
            }
        }
    #
    my @cmd_result;
    my $result = 'nok';
    my $reboot_flag;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    #print Dumper(\@commands_list);
    @cmd_result = $self->cmd(@commands_list);
    my $prompt = $self->latest_prompt;
    if($cmd_result[1] !~ m/$error_tips/i) {
        LogInfo("Edit cluster member success");
        $result = 'ok';
        } else {
        #can't enter config mode, maybe error or in <non-mater> mode
        LogInfo("Edit cluster member fail dut to $result[1]");
        
        }
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return $result;
    }

sub cluster_interface {
    my $self = shift;
    my %arg_hash = @_;
    my %arg_check = qw (management 1 primary 1 secondary 1);
    my @commands_list;
    push(@commands_list,'config');
    #do arg check here
    foreach my $arg (keys %arg_hash) {
        unless (exists $arg_check{$arg}) {die LogInfo("Unknow ARG name $arg")}
        unless ($arg_hash{$arg} =~ m/\d/) {die LogInfo("interface number invalid,set 0~7")}
        my $command = "cluster interface $arg $arg_hash{$arg}";
        push(@commands_list, $command);
        }
    #
    my @cmd_result;
    my $result = 'nok';
    my $reboot_flag;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    @cmd_result = $self->cmd(@commands_list);
    my $prompt = $self->latest_prompt;
    if($cmd_result[1] !~ m/$error_tips/i) {
        LogInfo("Config cluster interface success");
        $result = 'ok';
        } else {
        #can't enter config mode, maybe error or in <non-mater> mode
        LogInfo("config cluster interface fail dut to $cmd_result[1]");
        
        }
        if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
        return $result;
    }

# /*!   
#    @function is_cluster_master
#    @param 
#    @return $result  $result:0/1. 
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  check if the dut is cluster master</pre>
#    <pre>  0: not cluster master</pre>
#    <pre>  1: cluster master</pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#    <pre> my $result = $dut->cli->is_cluster_master();  
#    <pre> if($result eq '0'){  
#    <pre>    print "this dut is not cluster master";  
#    <pre> }    </pre>
# */
sub is_cluster_master {
    my $self = shift;
    $self->mode('console');
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    my $prompt = $self->latest_prompt;
    if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
    if($prompt eq 'WG<master>#') {
        return 1;
        }
    return 0;
    }

sub reboot {
    my $self = shift;
    $self->mode('console');
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    my $result = 'ok';
    my @cmd_result = $self->cmd('reboot','yes');
    my $exp = $self->handle;
    my $timeout = 100;
    $exp->expect($timeout,
                         [
                          qr'Restarting system',
                          sub{
                            my $fh = shift;
                            LogInfo("appliance is rebooting...,wait for 60 seconds");
                            sleep (60);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
                         [
                          qr'Rebooting',
                          sub{
                            my $fh = shift;
                            LogInfo("appliance is rebooting...,wait for 60 seconds");
                            sleep (160);
                            $fh->send("\r");
                            exp_continue,
                            }
                         ],
               [
                qr/[Ll]ogin:/,
                sub {
                    LogInfo("appliance reboot success");
                  $result = 'ok';
                }
               ],
               [
                eof =>
                sub {
                    LogInfo("EOF branch");
                  $result="nok";
                }
               ],
               [
                timeout =>
                sub {
                                    $result="nok";
                    $exp->hard_close();
                    print "WATS-CLI-TIMEOUT\n";
                 
                }
               ]
              );
   
   
   if($if_new_handle){
         $self->disconnect;
       } else{
         $self->switch_to_cli;
       }
   return $result;
    }
# /*!
#    @function add_vpn_user
#    @param (username, password, user_group_name)
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s) 
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>add user to specified user group.  
#         If ok, means user have been added to user_group successfully.
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(HostIP=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);   
#          my \@result = $dut1->cli->add_vpn_user(automatin,password,SSLVPN-Users)</pre>
#
# */
sub add_vpn_user {
    my $self = shift; 
    my ( $username,$password,$user_group) = @_;  
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if($user_group eq ""){
        $user_group = "SSLVPN-Users";
        $self->cmd(
               "config",
               "policy",
               "users $username $password 28800 1800 group $user_group",
               "apply",
        );
    }else{
        my @result_cmd =  $self->cmd(
               "config",
               "policy",
               "users $username $password 28800 1800",
               "user-group $user_group membership $username",
               "apply",
            );
        if ($result_cmd[1] =~ /Duplicate SSLVPN-Users exists./) {
            $self->cmd(
                   "config",
                   "policy",
                   "users $username $password 28800 1800 group $user_group",
                   "apply",
            );
        }
    }
    $self->disconnect;      
    $self->connect_cli; 
    my @result_cmd = $self->cmd("configure", "policy","show user-group $user_group");
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    if( !($result_cmd[1] =~ /.*User Group <$user_group>(.*\s)+Member(.*\s)+$username/) ){
        $result_cmd[0] = "nok";
        LogInfo("add sslvpn user failed!!");
    }    
    return @result_cmd;
} ### end of add_vpn_user
    

# /*!
#    @function config_sslvpn
#    @param (auth_ser=>'Firebox-DB', primaryIP=>$primaryIP[, backupIP=>$backupIP], resource_net=>'x.x.x.x/x'[, resource_ip=>'x.x.x.x', netmask=>'x.x.x.x'], [appliance_route=>'ture'/force_traffic=>'true',]ipaddrPool=>'x.x.x.x/x' ) 
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>config sslvpn firebox IP address, sslvpn allowed resources(the network or host that behind of firebox) and the virtual IP address pool. ipaddrPool attribute is mandatory, it will combine with one format of first 4 attributes.  
#       Config a DUT/XTM as SSL_muvpn. If return ok, means config allowed resource and virtual ip address success.</pre>
#    <h2>Example:</h2>
#    <pre>my $dut=new XTM(eth1=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);   
#          my \@result = $dut->cli->config_sslvpn(auth_ser=>'Firebox-DB', primaryIP=>'192.168.16.10', resource_net=>['192.168.1.0/24','192.168.2.0/24'], ipaddrPool=>'172.16.0.0/16', username=>'automation_ssl', password=>'password' ); 
#          my \@result = $dut->cli->config_sslvpn(auth_ser=>'Firebox-DB',primaryIP=>'192.168.16.10',backupIP=>'192.168.16.10',resource_ip=>['192.168.1.2','192.168.1.3'],netmask=>['255.255.255.0','255.255.255.0'],ipaddrPool=>'172.16.0.0/16',username=>'automation_ssl',password=>'password' );
#          my \@result = $dut->cli->config_sslvpn(auth_ser=>'Firebox-DB',primaryIP=>'192.168.16.10',appliance_route=>'enable',ipaddrPool=>'172.16.0.0/16',username=>'automation_ssl',password=>'password');
#          my \@result = $dut->cli->config_sslvpn(auth_ser=>["Firebox-DB","LDAP"],primaryIP=>'192.168.16.10',force_traffic=>'enable',ipaddrPool=>'172.16.0.0/16',username=>'automation_ssl',password=>'password');
#          my \@result = $dut->cli->config_sslvpn(auth_ser=>'Firebox-DB',primaryIP=>'192.168.16.10',force_traffic=>'enable',ipaddrPool=>'172.16.0.0/16',username=>'automation_ssl',password=>'password',protocol=>'protocol',configport=>'configport',config_channel_port=>'config_channel_port',domain=>'domain',servers=>'servers',auth=>'auth',encryption=>'encryption',interval=>'interval',timeout=>'timeout',);</pre>
# */

sub config_sslvpn {
    my $self = shift; 
    my %params = @_; 
    my $i = undef;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    #######################################################
    ### 1. configure auth Server and Firebox IP address ###
    #######################################################
    
    if( exists $params{'auth_ser'} && exists $params{'primaryIP'}){
        if(exists $params{'backupIP'}){
            $self->config_sslvpn_firebox_ip_address(auth_ser=>$params{'auth_ser'}, primaryIP=>$params{'primaryIP'},backupIP=>$params{'backupIP'});
        }else{
            $self->config_sslvpn_firebox_ip_address(auth_ser=>$params{'auth_ser'}, primaryIP=>$params{'primaryIP'});
        }
    }else{
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error Parameter in config_sslvpn: Primary IP and auth_ser must be provided.";
        LogInfo("Primary IP and auth_ser must be provided.");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }
    #####################################
    ### 2. configure allowed resource ###
    #####################################
    $self->switch_to_cli;
    if(exists $params{'resource_net'} ){
          my $resource_net = $params{'resource_net'};
        my $length = @$resource_net;
        $self->cmd( 
             "config",
             "policy",
        );
       for($i = 0;$i < $length;$i++){
           print("The resource :@$resource_net\n");
           @result_cmd = $self->cmd(
            "sslvpn resource user-route @$resource_net[$i]",
            );
#            my @ip_mask = split( /\//,@$resource_net[$i]);
#            ### match ip network address and ip address pool to verify the command 
#            if( !($result_cmd[1] =~ /\d(\s)+$ip_mask[0](\s)+\/$ip_mask[1]/ ) ){
#                $result_cmd[0] = "nok";
#                LogInfo("Command network_pool execute not take effert!!");
#                if($if_new_handle){
#                    $self->disconnect;
#                }else{
#                    $self->switch_to_cli;
#                }
            }
        $self->cmd( 
             "apply",
             "show sslvpn"
        );              

    }elsif(exists $params{'resource_ip'} && exists $params{'netmask'}){ 
        my $resource_ip = $parmas{'resource_ip'};
        my $netmask_ip = $parmas{'netmask'};
        my $length_ip = @$resource_ip;
        my $length_mask = @$netmask_ip;
        if($length_ip != $length_mask){
            LogInfo("There number of the IP and mask does not match!!");    
            $result_cmd[0] = "nok";
            $result_cmd[1] ="Error number of the parameters IP and mask.";
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd;
        }      
        $self->cmd( 
             "config",
             "policy",
        );
       print("The resource :@$resource_ip\n");
       for($i = 0;$i < $length_ip;$i++){
           @result_cmd = $self->cmd(
               "sslvpn resource user-route @$resource_ip[$i] @$netmask_ip[$i]"
           );           
       }
       $self->cmd(
           "apply",
           "show sslvpn" 
       );
        ### match ip network address and ip address pool to verify the command 
#        if( !($result_cmd[1] =~ /\d(\s)+$params{'resource_ip'}/) ){
#            $result_cmd[0] = "nok";
#            LogInfo("Command 'sslvpn resource user-route execute' not take effert!!");
#                $self->disconnect;
#            }else{
#                $self->switch_to_cli;
#            }
#            return @result_cmd;
#        }
    }elsif( exists($params{'appliance_route'}) && $params{'appliance_route'} eq 'enable' ){
        @result_cmd=$self->cmd( 
             "config",
             "policy",
             "sslvpn resource appliance-route",
             "apply",
             "show sslvpn" 
        );
        ### match ip network address and ip address pool to verify the command 
        if( !($result_cmd[1] =~ /Allowed Resources.*Trusted.*Optional.*VLANs/) ){
            $result_cmd[0] = "nok";
            LogInfo("Command appliance-route execute not take effert!!");
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd;
        }
    }elsif( $params{'force_traffic'} eq 'enable' ){
        @result_cmd=$self->cmd( 
             "config",
             "policy",
             "sslvpn resource force-traffic",
             "apply",
             "show sslvpn"
        );
        ### match ip network address and ip address pool to verify the command 
        if( !($result_cmd[1] =~ /Allowed Resources.*Force all client traffic through tunnel/ ) ){
            $result_cmd[0] = "nok";
            LogInfo("Command network_pool execute not take effert!!");
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }            
            return @result_cmd;
        }
    }else {
        LogInfo("invalid parameter in config_sslvpn: resource_net=>'x.x.x.x/x', [resource_ip=>'x.x.x.x', netmask=>'x.x.x.x'], [appliance_route=>'ture'/force_traffic=>'true',]ipaddrPool=>'x.x.x.x/x',username=>'xxxx', password=>'password' format ");
        $result_cmd[0] = "nok";
        $result_cmd[1] = "invalid parameter in config_sslvpn: resource_net=>'x.x.x.x/x', [resource_ip=>'x.x.x.x', netmask=>'x.x.x.x'], [appliance_route=>'ture'/force_traffic=>'true',]ipaddrPool=>'x.x.x.x/x',username=>'xxxx', password=>'password' format ";
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }
    ######################################
    ### 3. configure virtual ip address###
    ######################################
    if( exists $params{'ipaddrPool'} ){
        @result_cmd=$self->cmd( 
             "sslvpn address-pool ".$params{'ipaddrPool'},
             "sslvpn enable",
             "apply",
             "show sslvpn"
        );
        if( !($result_cmd[1] =~ /IP Address Pool(\s)+: $params{'ipaddrPool'}/) ){
            $result_cmd[0] = "nok";
            $result_cmd[1] = "config sslvpn  virtual IP pool execute fail!!";
            LogInfo("config sslvpn  virtual IP pool execute fail!!");
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd;
        }
    }else{
        LogInfo("not provide virtual IP pool when configure sslvpn!!");
        $result_cmd[0] = "nok";
        $result_cmd[1] = "not provide virtual IP pool when configure sslvpn!!";
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }
    #######################################
    ### 4. config the protocol and port ###
    ####################################### 
    if( exists $params{'protocol'} && exists $params{'configport'} ){
          if(exists $params{'config_channel_port'} && $params{'protocol'} =~ m/udp/i){
            @result_cmd=$self->cmd( 
                 "sslvpn protocol $params{'protocol'} $params{'configport'}",
                 "sslvpn config-port $params{'config_channel_port'}",
                 "sslvpn enable",
                 "apply",
                );  
            $self->disconnect;
            sleep(2);
            $self->connect_cli;
            @result_cmd=$self->cmd("show sslvpn");      
        }else{
            @result_cmd=$self->cmd(
                 "sslvpn protocol $params{'protocol'} $params{'configport'}",
                 "sslvpn enable",
                 "apply",
                );  
            $self->disconnect;  
            sleep(2);           
            $self->connect_cli;
            @result_cmd=$self->cmd("show sslvpn"); 
        }
    }elsif(exists $params{'config_channel_port'}){
        @result_cmd=$self->cmd( 
             "sslvpn config-port $params{'config_channel_port'}",   
             "sslvpn enable",            
             "apply",
            );  
        $self->disconnect;      
        $self->connect_cli;
        @result_cmd=$self->cmd("show sslvpn"); 
    } elsif( !exists $params{'protocol'} && !exists $params{'configport'} && !exists $params{'config_channel_port'} ) {
        LogInfo("Use the default procotol and port.");
    } elsif( exists $params{'protocol'} && !exists $params{'configport'}) {
        LogInfo("No port parameters when config the sslvpn protocol!!");
        $result_cmd[0] = "nok";
        $result_cmd[1] = "No port parameters when config the sslvpn protocol!!";
        return @result_cmd   ; 
    }
    ##########################
    ### 5. config the dns  ###
    ##########################  
    if( exists $params{'domain'}){
        LogInfo("Config the dns domain-name.");
        @result_cmd=$self->cmd( 
             "sslvpn dns domain-name $params{'domain'}",
             "sslvpn enable",
             "apply",
        );      
    }
    if( exists $params{'servers'} ){
        LogInfo("Config the dns servers.");
        @result_cmd=$self->cmd( 
             "sslvpn dns servers $params{'servers'}",
             "sslvpn enable",
             "apply", 
        );           
    }
    ################################
    ### 6. config the algorithm  ###
    ################################    
    if( exists $params{'auth'}){
        LogInfo("Config the algorithm authentication.");
        @result_cmd=$self->cmd( 
             "sslvpn algorithm authentication $params{'auth'}",
             "sslvpn enable",
             "apply",
        );      
    }
    if( exists $params{'encryption'} ){
        LogInfo("Config the algorithm encryption.");
        @result_cmd=$self->cmd( 
             "sslvpn algorithm encryption $params{'encryption'}",
             "sslvpn enable",
             "apply", 
        );           
    }   
    ################################
    ### 7. config the keepalive  ###
    ################################    
    if( exists $params{'interval'}){
        LogInfo("Config the keepalive interval.");
        @result_cmd=$self->cmd( 
             "sslvpn keepalive interval $params{'interval'}",
             "sslvpn enable",
             "apply",
        );      
    }
    if( exists $params{'timeout'} ){
        LogInfo("Config the keepalive timeout.");
        @result_cmd=$self->cmd( 
             "sslvpn keepalive timeout $params{'timeout'}",
             "sslvpn enable",
             "apply",  
        );           
    }   
    #################################
    ### 8. config the renegotiate ###
    #################################   
    if( exists $params{'renegotiate'}){
        LogInfo("Config the renegotiate data channel interval, in minute.");
        @result_cmd=$self->cmd( 
             "sslvpn renegotiate $params{'renegotiate'}",
             "sslvpn enable",
             "apply",
        );      
    }
     #################################
     ### 9. config the wins server ###
     #################################   
     if( exists $params{'wins'}){
         LogInfo("Config the wins server.");
         @result_cmd=$self->cmd(
              "sslvpn wins servers $params{'wins'}",
              "sslvpn enable",
              "apply",
         );
     }
#    ##################################
#    ###     4. add user            ###
#    ##################################
#    if( exists $params{'username'} && exists $params{'password'} ){
#        @result_cmd = $self->add_vpn_user($params{'username'},$params{'password'},'SSLVPN-Users');
#    }else{
#        LogInfo("not provide user name and password when configure sslvpn!!");
#        $result_cmd[0] = "nok";
#        $result_cmd[1] = "not provide user name and password when configure sslvpn!!";
#        if($if_new_handle){
#            $self->disconnect;
#        }else{
#            $self->switch_to_cli;
#        }
#        return @result_cmd;
#    }
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return @result_cmd; 
}## end of config_sslvpn
#  
#    @function config_sslvpn_firebox_ip_address
#    @param (auth_ser=>'Firebox-DB', primaryIP=>$primaryIP, backupIP=>$backupIP )
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s) 
#    @discussion 
#    <h2>Description:  </h2> 
#    <pre>config sslvpn authentication server and Firebox IP address.  
#         If ok, means config authentication server and Firebox IP address success. </pre>
#    <h2>Example:</h2>
#    <pre> my $dut=new XTM(eth1=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);
#    <pre>my \@result = $dut1->cli->config_sslvpn_firebox_ip_address(auth_ser=>'Firebox-DB', primaryIP=>$primaryIP) 
#         my \@result = $dut1->cli->config_sslvpn_firebox_ip_address(auth_ser=>'Firebox-DB', primaryIP=>$primaryIP, backupIP=>$backupIP)</pre>
#
#  
sub config_sslvpn_firebox_ip_address {
    my $self = shift; 
    my %params = @_;
    my $auth_ser = $params{'auth_ser'};
    my $length = @$auth_ser;
    my $i = undef;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    $self->cmd( 
           "config",
           "policy",
           "sslvpn primary ".$params{'primaryIP'}
    );
    if ($length != 0) {
        for($i = 0;$i < $length;$i++){
            print("The auth_ser :@$auth_ser\n");
            $self->cmd(
                "sslvpn auth-server @$auth_ser[$i]",
            );
        }
    }else{
          if ($auth_ser ne 'Firebox-DB'){
            $self->cmd(
                "sslvpn auth-server $auth_ser default",
            );
        }               
    }
    if (exists $params{'backupIP'} ){
        $self->cmd( 
           "sslvpn backup ".$params{'backupIP'}
        );
    }
    my @result_cmd = $self->cmd(      
           "apply",
           "show sslvpn"
    );    
    if($result_cmd[1] =~ /Authentication Server(\s)*: Firebox-DB(.*\s)+Firebox Primary IP(\s)*: $params{'primaryIP'}/ ){
        ## match auth_server and primary IP address. 
        if (exists $params{'backupIP'}) {
            ## match bakupIP 
            if( $result_cmd[1] =~ /Firebox Backup IP(\s)*: $params{'backupIP'}/){
                $result_cmd[0] = 'ok';               
            }else{
                $result_cmd[0] = "nok";
            }            
        }else{
            $result_cmd[0] = "ok";
        }
    }else{
        $result_cmd[0] = "nok";
    }
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return  @result_cmd ; 
} ## end of config_sslvpn_firebox_ip_address


# /*!
#    @function config_muvpn_ipsec
#    @param (ipsecName=>'ipsecVpn', primaryIP=>$primaryIP[, backupIP=>$backupIP], auth_server=>'Firebox-DB|RADIUS|LDAP|SecurID', passphrase=>'password', force_all_traffic=>'yes/no', resource_net=>'x.x.x.x/x'[, resource_net=>'x.x.x.x'], host_ip=>'x.x.x.x'[, range_ip=>'x.x.x.x-x.x.x.x'],username=>'automation_ipsec', password=>'password',client_type=>"Shrew-Soft-Client", config_file_name=>'ipsec.vpn') 
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s). $result[2]:configuration file full path
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>config IPsec muvpn firebox IP address, allowed resources(the network or host that behind of firebox) and the virtual IP address pool. 
#       ATTENDTIONG: 'host_ip' and 'range_ip' are two form of address for virtual address. host_ip and range_ip are two form of XTM private subnet or host.
#       Config a DUT/XTM as IPSec_muvpn.  
#       If ok, means config IPsec server on firebox success. </pre>
#    <h2>Example:</h2>
#    <pre>my $dut=new XTM(eth1=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);   
#		   my \@result = $dut->cli->config_muvpn_ipsec(ipsecName=>'ipsecVpn',primaryIP=>$dut_eth0_ip, auth_server=>'Firebox-DB', passphrase=>$password,force_all_traffic=>'no',resource_net=>$resourceNet, range_ip=>$addressPool,username=>$userName, password=>$password, client_type=>"Shrew-Soft-Client", config_file_name=>'ipsec.vpn');
#          my \@result = $dut->cli->config_muvpn_ipsec(ipsecName=>'ipsecVpn',primaryIP=>$primaryIP, backupIP=>$backupIP,auth_server=>'Firebox-DB', passphrase=>'password',force_all_traffic=>'no',resource_net=>'x.x.x.x/x',range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_ssl', password=>'password' );
#          my \@result = $dut->cli->config_muvpn_ipsec(ipsecName=>'ipsecVpn',primaryIP=>$primaryIP,auth_server=>'Firebox-DB', passphrase=>'password',force_all_traffic=>'no',resource_net=>'x.x.x.x',host_ip=>'x.x.x.x',username=>'automation_ssl', password=>'password' );</pre>

# */
sub config_muvpn_ipsec {
    my $self = shift; 
	
    my %params = @_;   
    my @result_cmd;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;	
    $self->switch_to_cli;
		
    ## 1. check parameters.
    if( ! (exists $params{'ipsecName'} && exists $params{'primaryIP'} && exists $params{'auth_server'} && exists $params{'passphrase'} &&exists $params{'force_all_traffic'} && exists $params{'resource_net'} && (exists $params{'host_ip'} || exists $params{'range_ip'} ) && exists $params{'username'} && exists $params{'password'} ) ){
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters in config_muvpn_ipsec! ipsecName,primaryIP,auth_server,passphrase,force_all_traffic,resource_net,host_ip(or range_ip),username and password are mandatory.";
        LogInfo("Error parameters! ipsecName,primaryIP,auth_server,passphrase,force_all_traffic,resource_net,host_ip(or range_ip),username and password are mandatory.");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd; 
    }
    ## 2. compose command string
    my $command_str1;
    my $command_str2;
    my @range;
    if( exists $params{'host_ip'} && exists $params{'range_ip'} ){
       @range= split (/-/,$params{'range_ip'});
       $command_str = "auth-server $params{'auth_server'} $params{'passphrase'} $params{'force_all_traffic'} $params{'resource_net'} host-ip $params{'host_ip'} range-ip $range[0] $range[1]";
    }elsif(exists $params{'host_ip'} ){
       $command_str = "auth-server $params{'auth_server'} $params{'passphrase'} $params{'force_all_traffic'} $params{'resource_net'} host-ip $params{'host_ip'}";
    }elsif(exists $params{'range_ip'} ){
       @range= split (/-/,$params{'range_ip'});
       $command_str = "auth-server $params{'auth_server'} $params{'passphrase'} $params{'force_all_traffic'} $params{'resource_net'} range-ip $range[0] $range[1]";
    }else{
       print "Error: host_ip and range_ip are not provided.\n"; 
    }
   
    if( exists $params{'backupIP'} ){
        $command_str2 = "firebox-ip primary $params{'primaryIP'} backup $params{'backupIP'}";
    }else{
        $command_str2 = "firebox-ip primary $params{'primaryIP'}";
    }
    
    ## 3. execute command.	
	my $filename = $params{'config_file_name'}; 
	if( !exists $params{'client_type'} ){
		my $xtmSerial = $self->obtain_serial;
		my $filename = "IPsecCfg_".$xtmSerial.".wgx.ini";
		my $ftpServerIP =_local_ip();
		$self->switch_to_cli;  
		@result_cmd=$self->cmd( 
			 "config",
			 "policy",
			 "no mvpn-ipsec ".$params{'ipsecName'},
			 "mvpn-ipsec ".$params{'ipsecName'},
			 $command_str,
			 $command_str2,
			 "apply",
			 "show mvpn-ipsec",
			 "exit",
			 "exit",
			 "exit",
			 "export muvpn $params{'ipsecName'} to ftp://automation:password\@$ftpServerIP//tmp/$filename" 
		);
	} else {
		my $ftpServerIP =$self->_control_ip();		
		$self->switch_to_cli;  
		@result_cmd=$self->cmd( 
			 "config",
			 "policy",
			 "no mvpn-ipsec ".$params{'ipsecName'},
			 "mvpn-ipsec ".$params{'ipsecName'},
			 $command_str,
			 $command_str2,
			 "apply",
			 "show mvpn-ipsec",
			 "exit",
			 "exit",
			 "exit",
			 "export muvpn $params{'ipsecName'} client-type $params{'client_type'} to ftp://automation:password\@$ftpServerIP//tmp/$filename" 
		);
	}
	
    if(!$result_cmd[1]=~/$params{'ipsecName'}.*$params{'auth_server'}.*$params{'resource_net'}/){
        LogInfo("Error: configure Firebox to ipsec server failed!");
        $result_cmd[0] = 'nok';
        $result_cmd[1] = "Error: configure Firebox to ipsec server failed!";
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd; 
    }
    ## add vpn user
    @result_cmd = $self->add_vpn_user($params{'username'},$params{'password'},$params{'ipsecName'});
    if ($result_cmd[0] eq 'nok'){
        LogInfo("Error: add user to ipsec user-group failed!");
        $result_cmd[1] = "Error: add user to ipsec user-group failed!";
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }
      
	if (!exists $params{'client_type'}) {
		### move cfg file to RunTimeDirectory
		my $cfg_source_file = $tmp_dir."/".$filename;
		my $cfg_dest_file = $config->getRunTimeDirectory."/Temp/$filename";
		eval {
			`mv -f $cfg_source_file $cfg_dest_file`;
		};
		if($@){
			LogInfo("Error: mv config file failed:$@\n");
			$result_cmd[0] = 'nok';
			$result_cmd[1] = "Error: mv config file failed:$@\n";       
		}else{
			$result_cmd[0] = 'ok';
			$result_cmd[2] = $cfg_dest_file;
		}
		if($if_new_handle){
			$self->disconnect;
		}else{
			$self->switch_to_cli;
		}
	} 	
	
	return @result_cmd;
} ## end of config_muvpn_ipsec

# /*!
#    @function config_l2tp
#    @param (ipsec=>'disable/enable',host_ip=>\@host,range_ip=>'x.x.x.x-x.x.x.x',network_ip=>'x.x.x.0/24') 
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#   <h2>Description: </h2> 
#   <pre>config l2tp virtual IP address pool and active it. 
#        ATTENDTIONG: 'host_ip' and 'range_ip' are two form of address for virtual address.
#        Config a DUT/XTM as l2tp server.  If result ok, means config l2tp server on firebox success.(not include adding policy) </pre>
#   <h2>Example:</h2>
#   <pre>my $dut=new XTM(eth1=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#         my \@result = $dut->cli->config_l2tp(ipsec=>'disable',host_ip=>\@array);
#         my \@result = $dut->cli->config_l2tp(ipsec=>'disable',range_ip=>'x.x.x.x-x.x.x.x');
#         my \@result = $dut->cli->config_l2tp(ipsec=>'enable',network_ip=>'x.x.x.0/24');
#         my \@result = $dut->cli->config_l2tp(ipsec=>'enable',network_ip=>'x.x.x.0/24',nat_traversal=>'yes/no',p1_encrypt=>'DES-3',p1_hash=>'sha1',p1_dh_group=>'2');
#         my \@result = $dut->cli->config_l2tp(ipsec=>'disable',network_ip=>'x.x.x.0/24',mtu=>'1400', mru=>'1400',retry_time=>'3',max_retry=>'3');</pre>
#         must   config  config_l2tp(ipsec=>'disable',host_ip=>\@array)
#                        config_l2tp(ipsec=>'disable',range_ip=>'x.x.x.x-x.x.x.x')
#                        config_l2tp(ipsec=>'disable',network_ip=>'x.x.x.0/24')
#         choose config  config_l2tp(ipsec=>'enable',network_ip=>'x.x.x.0/24',domain=>'radius',auth_user=>'test1',user_type=>'RADIUS')
#                        config_l2tp(ipsec=>'enable',network_ip=>'x.x.x.0/24',p2_encrypt=>'AES',p2_hash=>'MD5',p2_mode=>'ESP')
# */
sub config_l2tp {
    my $self = shift;  
    my %args = @_;
    my $proposal;
    my @result;
    my @range;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if(!exists($args{'ipsec'})) {
        print("\nconfig_l2tp: wrong l2tp parameters: must configure ipsec option.\n");
        $result[0] = "nok";
        return @result; 
    }
    if(!exists($args{'host_ip'}) and !exists($args{'range_ip'}) and !exists($args{'network_ip'})) {      
        print("\nconfig_l2tp: wrong l2tp parameters: miss configure l2tp virtual ip pool.\n");
        $result[0] = "nok";
        return @result; 
    }
    if($args{'ipsec'} ne 'enable' and $args{'ipsec'} ne 'disable'){
        print("\nconfig_l2tp: ipsec option should only be disable or enable");
        $result[0] = "nok";
        return @result; 
    }
    my @config = (
        "config",
        "policy"
    );
    if(exists($args{'host_ip'})) {
        if( ref($args{'host_ip'}) ne 'ARRAY'){
            print("\nconfig_l2tp: host_ip should be a array refrence, and there should be at least 2 ip address in ip pool.");
            $result[0] = "nok";
            return @result; 
        }
        my $hosts=$args{'host_ip'};
        my @host_list;
        foreach $ele ( @$hosts ){
            push(@host_list,"host $ele");
        }
        push(@config,"l2tp address-pool @host_list");
        push(@config,'yes');
        push(@config,'apply');
    }
    if(exists($args{'range_ip'})) {
        @range= split (/-/,$args{'range_ip'});
        if($range[0] eq "" or $range[1] eq "" or scalar(@range) ne 2){
            print("\nconfig_l2tp: wrong l2tp ip range, should be x.x.x.x-x.x.x.x format.");
            $result[0] = "nok";
            return @result; 
        }
        push(@config,"l2tp address-pool range $range[0] $range[1]");
        push(@config,'yes');
        push(@config,'apply');
    }
    if(exists($args{'network_ip'})) {
        push(@config,"l2tp address-pool network $args{'network_ip'}");
        push(@config,'yes');
        push(@config,'apply');
    }
    push(@config,'l2tp enable');
    push(@config,'apply');
    if($args{'ipsec'} eq 'disable') {
        push(@config,"no l2tp ipsec enable");
        push(@config,'apply');
    }
    if(exists($args{'domain'})) {
        push(@config,"l2tp auth-server $args{'domain'} default");
    }
    if(exists($args{'mtu'})) {
        push(@config,"l2tp mtu $args{'mtu'}");
    }
    if(exists($args{'mru'})) {
        push(@config,"l2tp mru $args{'mru'}");
    }
    if(exists($args{'max_retry'})) {
        push(@config,"l2tp max-retries $args{'max_retry'}");
    }
    if(exists($args{'retry_time'})) {
        push(@config,"l2tp retransmit-timeout $args{'retry_time'}");
    }
    if(exists($args{'keepalive'})) {
        push(@config,"l2tp keep-alive-interval $args{'keepalive'}");
    }
    push(@config,'apply');
    if(exists($args{'auth_group'}) and !exists($args{'group_type'})) {
        print("\nconfig_l2tp: auth_group and group_type must exist at same time.\n");
        $result[0] = "nok";
        return @result; 
    } elsif (exists($args{'auth_group'})) {
        push(@config,"l2tp auth-user-group specify-user-group group $args{'auth_group'} $args{'group_type'}");
    }
    if(exists($args{'auth_user'}) and !exists($args{'user_type'})) {
        print("\nconfig_l2tp: auth_user and user_type must exist at same time.\n");
        $result[0] = "nok";
        return @result; 
    } elsif (exists($args{'auth_user'})) {
        push(@config,"l2tp auth-user-group specify-user-group user $args{'auth_user'} $args{'user_type'}");
    }
    push(@config,'apply');
    if($args{'ipsec'} eq 'enable') {
        if(!exists($args{'preshare_key'}) && (!exists($args{'certid'}) || !exists($args{'certname'}))) {
            print("\nconfig_l2tp: must add ipsec preshare-key or cert when enable ipsec.\n");
            $result[0] = "nok";
            return @result;
        }
        push(@config,"l2tp ipsec enable");
        if(exists($args{'preshare_key'})) {
            push(@config,"l2tp ipsec phase1 pre-shared $args{'preshare_key'}");
            push(@config,"apply");
        }
        if(exists($args{'certid'})) {
            push(@config,"l2tp ipsec phase1 certificate $args{'certid'} \"$args{'certname'}\" rsa");
            push(@config,"apply");
        }
        if(exists($args{'nat_traversal'})) {
            if ($args{'nat_traversal'} eq 'yes') {
                push(@config,"l2tp ipsec phase1 nat-traversal enable");
                push(@config,"apply");
            } elsif ($args{'nat_traversal'} eq 'no') {
                push(@config,"no l2tp ipsec phase1 nat-traversal");
                push(@config,"apply");
            } else {
                print("\nconfig_l2tp: nat_traversal parameter just can be yes or no.\n");
                $result[0] = "nok";
                return @result;
            }
        }
        if (!exists($args{p1_encrypt})) {
            $args{p1_encrypt} = "DES-3";
        }
        if (!exists($args{p1_hash})) {
            $args{p1_hash} = "sha1";
        }
        if (!exists($args{p1_dh_group})) {
            $args{p1_dh_group} = "2";
        }
        push(@config,"l2tp ipsec phase1 transform 1 $args{p1_hash} $args{p1_encrypt} 8 Diffie-Hellman-Group$args{p1_dh_group}");
        push(@config,"apply");
        if (exists($args{p2_pfs})) {
            if ($args{p2_pfs} eq 'no') {
                push(@config,"no l2tp ipsec phase2 pfs enable");
                push(@config,"apply");
            } elsif ($args{p2_pfs} =~m/\d/s) {
                push(@config,"l2tp ipsec phase2 pfs enable Diffie-Hellman-Group$args{p2_pfs}");
                push(@config,"apply");
            } else {
                print("\nconfig_l2tp: ipsec phase2 pfs should be no or integer.\n");
                $result[0] = "nok";
                return @result;
            }
        }
        if (!exists($args{p2_lifetime})) {
            $args{p2_lifetime} = "10000";
        }
        if (!exists($args{p2_encrypt})) {
            $args{p2_encrypt} = "3DES";
        }
        if (!exists($args{p2_hash})) {
            $args{p2_hash} = "SHA1";
        }
        if (!exists($args{p2_mode})) {
            $args{p2_mode} = "ESP";
        }
        $proposal = "${args{p2_mode}}-${args{p2_encrypt}}-${args{p2_hash}}";
        if ($args{p2_encrypt} eq 'AES192' or $args{p2_encrypt} eq 'AES256') {
            print("\nconfig_l2tp: ipsec phase2 pfs encryption algorithm doesn't support AES192 and AES256.\n");
            $result[0] = "nok";
            return @result;
        }
        if ($args{p2_mode} eq 'AH') {
            $proposal = "${args{p2_mode}}-$args{p2_hash}",
            push(@config, "proposal p2 $proposal ah transform $args{p2_lifetime} 24576 $args{p2_hash}");
            push(@config,"apply");
        }
        if (($args{p2_encrypt} eq 'AES128' or $args{p2_encrypt} eq 'AES') && $args{p2_mode} eq 'ESP') {
            $proposal = "ESP-AES128-${args{p2_hash}}",
            push(@config, "proposal p2 $proposal esp transform $args{p2_lifetime} 24576 aes128 ${args{p2_hash}}");  
            push(@config,"apply");
        }
        push(@config,'no l2tp ipsec phase2 proposal ESP-AES-SHA1');
        push(@config,"apply");
        if ($args{p2_mode} eq 'AH') {
            push(@config,"l2tp ipsec phase2 proposal $proposal replace yes");
        } else {
            push(@config,"l2tp ipsec phase2 proposal $proposal");
        }
        push(@config,"apply");
    }
    push(@config,'show l2tp');
    push(@config,'exit');
    @result = $self->cmd(@config);
    if($if_new_handle){
         $self->disconnect;
    } else{
         $self->switch_to_cli;
    }
    return @result;
}

# /*!
#    @function config_pptp
#    @param (host_ip=>\@host [, range_ip=>'x.x.x.x-x.x.x.x'],username=>'automation_ipsec', password=>'password') 
#    @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#    @discussion 
#   <h2>Description: </h2> 
#   <pre>config pptp virtual IP address pool and active it. 
#        ATTENDTIONG: 'host_ip' and 'range_ip' are two form of address for virtual address.
#        Config a DUT/XTM as pptp_muvpn.  If result ok, means config pptp server on firebox success.(not include adding policy) </pre>
#   <h2>Example:</h2>
#   <pre>my $dut=new XTM(eth1=>$dut1_ctr_ip,CLIMode=>'console',SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);  
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,username=>'automation_pptp', password=>'password' );
#         my \@result = $dut->cli->config_pptp(range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password' );
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password' );
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password', username2=>'automation_pptp', password2=>'password');
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password', auth_domain=>'auth_domain');
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password', pptp_mru=>'pptp_mru', pptp_mtu=>'pptp_mtu');
#         my \@result = $dut->cli->config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password' username2=>'automation_pptp', password2=>'password', pptp_mru=>'pptp_mru', pptp_mtu=>'pptp_mtu',auth_domain=>'auth_domain');</pre>
#         must   config  config_pptp(host_ip=>\@array,range_ip=>'x.x.x.x-x.x.x.x',username=>'automation_pptp', password=>'password'
#         choose config  username2=>'automation_pptp', password2=>'password', pptp_mru=>'pptp_mru', pptp_mtu=>'pptp_mtu',auth_domain=>'auth_domain'
# */
sub config_pptp {
    my $self = shift;
    
    my %params = @_;
    my @result_cmd;
    my @result_cmd1;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    ## 1. check parameters.
    if( ! ( (exists $params{'host_ip'} || exists $params{'range_ip'} ) && (exists $params{'username'} && exists $params{'password'}) || (exists $params{'username2'} && exists $params{'password2'}) || (exists $params{'pptp_mtu'} && exists $params{'pptp_mru'}) || (exists $params{'auth_domain'}) || (exists $params{'mppe'}) ) ){
       $result_cmd[0] = "nok";
       $result_cmd[1] = "Error parameters for config_pptp! host_ip(or range_ip),username and password are mandatory.";
       LogInfo("Error parameters! host_ip(or range_ip),username and password are mandatory.");
       return @result_cmd; 
    }
    ## 2. compose command string
    my $command_str1;
    my $command_str2;
    my @range;
    @result_cmd=$self->cmd( 
         "config",
         "policy",
         "no rule Ping.1",
         "no pptp enable",
         "apply"
    );
    if( exists $params{'host_ip'} && exists $params{'range_ip'} ){
        if( ref($params{'host_ip'}) ne 'ARRAY'){
            LogInfo("Error parameters! host_ip should be a array refrence, and there will be at least 2 ip address in the array.");
            $result_cmd[0]='nok';
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd; 
        }
        my $hosts=$params{'host_ip'};
        foreach $ele ( @$hosts ){
            @result_cmd=$self->cmd( "pptp pptp-address host $ele" );
        }
        
        @range= split (/-/,$params{'range_ip'});
        if($range[0] eq "" or $range[1] eq "" or scalar(@range) ne 2){
            LogInfo("Error parameters! range param error, should be x.x.x.x-x.x.x.x format.");
            $result_cmd[0]='nok';
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd; 
        }
        @result_cmd=$self->cmd( "pptp pptp-address range $range[0] $range[1]" );
        
    }elsif(exists $params{'host_ip'}){        
        ### only host_ip params.
        if( ref($params{'host_ip'}) ne 'ARRAY'){
            LogInfo("Error parameters! host_ip should be a array refrence, and there will be at least 2 ip address in the array.");
            $result_cmd[0]='nok';
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd; 
        }       
        my $hosts=$params{'host_ip'};
        foreach $ele ( @$hosts ){
            @result_cmd=$self->cmd( "pptp pptp-address host $ele" );
        }
    }elsif(exists $params{'range_ip'}){
        ### only range ip params.
        @range= split (/-/,$params{'range_ip'});
        if($range[0] eq "" or $range[1] eq "" or scalar(@range) ne 2){
            LogInfo("Error parameters! range param error, should be x.x.x.x-x.x.x.x format.");
            $result_cmd[0]='nok';
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @result_cmd; 
        }
        @result_cmd=$self->cmd( "pptp pptp-address range $range[0] $range[1]" );
    }else{
        print "Error: host_ip and range_ip are not provided or have bad format.\n"; 
    }
    
    ### apply configure and show configure.
    if( $result_cmd[0] eq 'ok'){
        @result_cmd=$self->cmd( "pptp enable","apply","show pptp");
        if ($result_cmd[0] ne 'ok' or !($result_cmd[1]=~m/state.*enable/s)){
            LogInfo("Verify result: Failed, config pptp does not take effort!.");
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }       
            return @result_cmd; 
        }  
        
        if(exists $params{'pptp_mtu'}){
            @result_cmd1=$self->cmd( "pptp pptp-mtu $params{'pptp_mtu'}");
    }else{
            @result_cmd1=$self->cmd( "pptp pptp-mtu 1400");
        }
        
        if(exists $params{'pptp_mru'}){
            @result_cmd1=$self->cmd( "pptp pptp-mru $params{'pptp_mru'}");
        }else{
            @result_cmd1=$self->cmd( "pptp pptp-mru 1400");
        }
        
        if(exists $params{'mppe'}){
            @result_cmd1=$self->cmd("pptp mppe $params{'mppe'}");   
        }else{
        
        }
        
        if(exists $params{'auth_domain'}){
            @result_cmd1=$self->cmd("pptp auth-domain $params{'auth_domain'}");
        }else{
        
        }
        @result_cmd1=$self->cmd( "pptp enable","apply","show pptp");
    }else{
        LogInfo("PPTP config fail!.");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd; 
    }
    
    ### verify configure result.
    if($result_cmd1[0] eq 'nok' or !($result_cmd1[1]=~m/state.*enable/s)){
        LogInfo("Verify result: Failed, config pptp does not take effort!.");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd1; 
    }

    ### add pptp user.
    if(exists $params{'username'} && exists $params{'password'}){
        my @ret_user = $self->add_vpn_user($params{'username'},$params{'password'},'PPTP-Users');
        if($ret_user[0] eq 'nok'){
            LogInfo("PPTP config first user fail!.");
            if($if_new_handle){
                $self->disconnect;
            }else{
                $self->switch_to_cli;
            }
            return @ret_user;
       }else{
           if(exists $params{'username2'} && exists $params{'password2'}){
               my @ret_user2 = $self->add_vpn_user($params{'username2'},$params{'password2'},'PPTP-Users');
               if($ret_user2[0] eq 'nok'){
                   LogInfo("PPTP-Users add the second user fail!.");
                   if($if_new_handle){
                       $self->disconnect;
                   }else{
                       $self->switch_to_cli;
                   }
                   return @ret_user2;
               }else{
                   LogInfo("PPTP-Users add the second user success!.");
                   return @ret_user2;
               }
           }else{
               LogInfo("PPTP-Users add one user success!.");
               return @ret_user;
           }
       }
       return @result_cmd;
    } ## end of config_pptp
}

# /*!
#   @function configBovpn
#   @param(remote_ip=>'x.x.x.x',[remote_ID_ip=>'x.x.x.x',remote_ID_domain_name=>'abc.com'|remote_ID_user_domain=>'cba.com'], local_ID_ip=>'x.x.x.x',[local_ID_domain_name=>'abc.com'|local_ID_user_domain=>'cba.com'], local_subnet=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x', remote_subnet=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x'[, gw_name=>"gateway.1", tunnel_name=>"tunnel.1", local_if_alias=>'External', pre_shared=>'11111111', mode=>'Aggressive|Main|Main-Fallback-Aggressive', method=>'MD5|SHA1',encry=>'AES|DES|3DES',sa_life=>'18800', encry_length=>'16|24|32',key_group=>'Diffie-Hellman-Group5|Diffie-Hellman-Group2|Diffie-Hellman-Group1',phase2_pfs=>'dh-group5|dh-group2|dh-group1',phase2_proposal_name=>'ESP-AES-SHA1|ESP-DES-MD5|...'[direction=>'bi-direction|inbound|outbound'],[dnat=>x.x.x.x],[one_one_nat=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x'],[broadcast_over_tunnel=>'enable'],[multicast_endpoint_local=>'x.x.x.x',multicast_endpoint_remote=>'x.x.x.x',multicast_origination_ip=>'x.x.x.x',multicast_group_ip=>'x.x.x.x', multi_input_intf/multi_output_intf=>'0-9'] ])
#   @return \@result  $result[0]:ok/nok. $result[1]:the output info of cmd(s)
#   @discussion 
#   <h2>Description: </h2>             
#   <pre>This method is aimed to config a firebox as bovpn gateway mode. This function need at least 4 parameters:remote_ip( or remote_ID_domain_name/remote_ID_user_domain/remote_ID_ip), local_ID(provide one form of local_ID_ip/local_ID_domain_name/local_ID_user_domain), local_subnet and remote_subnet.</pre>
#   <pre>If provide remote_ip, mean establish static bovpn, otherwise, establish dynamic bovpn. Default value will be first value above in param example. If only provide basic params, and vpn tunnel will be established use default params. If certain parameter need to be specified, just provide the parameter when invoke the function. </pre>
#   <h2>Example:</h2>
#   <pre>my $dut= new XTM('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#
#   <pre>STATIC bovpn example:
#   <pre>my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_ip=>'77.77.77.77', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_domain_name=>'remote.com', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_user_domain=>'remote.com', local_ID_domain_name=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');</pre>
#
#   <pre>STATIC bovpn with certificate authentication example:
#   <pre>my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24', $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_ip=>'77.77.77.77', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24', $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_domain_name=>'remote.com', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24', $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');
#        my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_user_domain=>'remote.com', local_ID_domain_name=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24', $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');</pre>
#
#   <pre>DYNAMIC bovpn example:
#   <pre>my \@result=$dut->cli->configBovpn( remote_ID_domain_name=>'remote.com', local_ID_domain_name=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',dns_ip=>'10.138.7.3');
#        my \@result=$dut->cli->configBovpn( remote_ID_user_domain=>'remote.com', local_ID_user_domain=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',dns_ip=>'10.138.7.3');</pre>
#
#   <pre>DYNAMIC bovpn with certificate authentication example:
#   <pre>my \@result=$dut->cli->configBovpn( remote_ID_domain_name=>'remote.com', local_ID_domain_name=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',dns_ip=>'10.138.7.3' $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');
#        my \@result=$dut->cli->configBovpn( remote_ID_user_domain=>'remote.com', local_ID_user_domain=>'local.com', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',dns_ip=>'10.138.7.3' $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');</pre>
#
#   <pre>dnat example:
#   <pre>my \@result=$dut->cli->configBovpn(local_ID_ip=>'192.168.11.34', remote_ip=>'192.168.77.33', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',dnat=>'192.168.11.34');
#
#   <pre>one_one_nat example:
#   <pre>my \@result=$dut->cli->configBovpn(local_ID_ip=>'192.168.11.34', remote_ip=>'192.168.77.33', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',one_one_nat=>'192.168.11.37/24');</pre>
#
#   <pre>multicast example:
#   <pre>my \@result=$dut->cli->configBovpn(local_ID_ip=>'192.168.11.34', remote_ip=>'192.168.77.33', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',multicast_endpoint_local=>'34.4.3.31',multicast_endpoint_remote=>'4.3.32.2',multicast_origination_ip=>'192.216.3.4',multicast_group_ip=>'224.0.0.2', multi_input_intf/multi_output_intf=>'1');</pre>

#   <pre>all bovpn parameters example:
#   <pre>my \@result=$dut->cli->configBovpn(remote_ip=>'192.168.77.33', remote_ID_domain_name=>'remote.com', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24',gw_name=>"bovpn_gateway",tunnel_name=>'bovpn_tunnel',local_if_alias=>'Optional-1', pre_shared=>'22222222',mode=>'Aggressive', Method=>'SHA1', encry=>'3DES', sa_life=>'3600', encry_length=>'128',key_group=>'Diffie-Hellman-Group2',phase2_pfs=>'dh-group2',phase2_proposal_name=>'ESP-DES-MD5',dnat=>'3.4.4.23',broadcast_over_tunnel=>'enable',resolve=>"yes",replace_proposal=>"replace_proposal $params{certificate_id}=>'20001', $params{certificate_type}=>'ip-address', $params{certificate_algorithm}=>'rsa', $params{certificate_string}=>'\"c=CN st=SC o=WG cn=WatchGuard\"');</pre>
#
# */

sub configBovpn{
    my $self=shift; 
    my %params=@_;
    my @result;
    my @cli_cmd;
    my $tmpStr;
    ###############0. prepare parameters before compose commands.############
    ##### mandatory params check


    
    
    ##### prepare remote gateway ID parameter with different format for dynamic bovpn
    ########20110727 - add version judgement by lizhi###################################
    my $yes = '';#if version >= 11.5,need yes when config doamin_name

    my $sys_info = $self->obtain_systeminfo();
    if ($sys_info ne 'nok') {
        if ($sys_info =~ m/version\s+:\s+11\.4/) {
            #$yes = 'yes';
        } else {
              $yes = $params{resolve} ? $params{resolve}: "yes"; 
        }
    }
    #####################################################################################
    if( (exists $params{remote_ID_ip} and exists $params{remote_ID_domain_name}) or
        (exists $params{remote_ID_domain_name} and exists $params{remote_ID_user_domain}) or
        (exists $params{remote_ID_ip} and exists $params{remote_ID_user_domain})){
        ## if provide remote_ip, mean estable static bovpn. Remote ID can not be provide more than twice.
        $result[0]='nok';
        $result[1]='ERROR: remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameters can not existed at the same time.';
        LogInfo("ERROR: remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameters can not existed at the same time.");
        return @result;
    }elsif( exists $params{remote_ID_domain_name} ){
        $params{remote_ID}="by-domain domain-name $params{remote_ID_domain_name}.wats $yes";
    }elsif( exists $params{remote_ID_user_domain} ){
        $params{remote_ID}="by-domain user-domain $params{remote_ID_user_domain}.wats";
    }elsif( exists $params{remote_ID_ip} ){
        $params{remote_ID}=$params{remote_ID_ip};
    }else{
        if( exists $params{'remote_ip'} ){
            ## if no any form of remote ID provide, but provided remote_ip, use remote_ip as ID.
            $params{remote_ID}=$params{remote_ip};
        }else{
            ## if no any form of remote ID and no remote_ip provided, report error.
            $result[0]='nok';
            $result[1]='dynamic bovpn configre need one of remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameter. But now there no parameters provide.';
            LogInfo("dynamic bovpn configre need one of remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameter. But now there no parameters provide.");
            return @result;
        }
    }
    ##### prepare local gateway ID parameter with different format
    if(exists $params{local_ID_ip} and exists $params{local_ID_domain_name} or
      (exists $params{local_ID_domain_name} and exists $params{local_ID_user_domain}) or
      (exists $params{local_ID_ip} and exists $params{local_ID_user_domain}) ){
        $result[0]='nok';
        $result[1]='local_ID_ip, local_ID_domain_name and local_ID_user_domain parameters can not existed at the same time.';
        LogInfo("local_ID_ip, local_ID_domain_name and local_ID_user_domain parameters can not existed at the same time.");
        return @result;
    }elsif( exists $params{local_ID_domain_name} ){
        $params{local_ID}="by-domain domain-name $params{local_ID_domain_name}.wats";
    }elsif( exists $params{local_ID_user_domain} ){
        $params{local_ID}="by-domain user-domain $params{local_ID_user_domain}.wats";
    }elsif( exists $params{local_ID_ip} ){
        $params{local_ID}=$params{local_ID_ip};
    }else{
        ## if no any form of local ID provide, use remote gateway ip as ID.
        $result[0]='nok';
        $result[1]='bovpn configre need one of local_ID_ip, local_ID_domain_name and local_ID_user_domain parameter. But now there no parameters provide.';
        LogInfo("bovpn configre need one of local_ID_ip, local_ID_domain_name and local_ID_user_domain parameter. But now there no parameters provide.");
        return @result;
    }
    
    ##### prepare local_subnet/remote_subnet parameter with different format    
    if(! (exists $params{remote_subnet} and exists $params{local_subnet}) ){
        $result[0]='nok';
        $result[1]='local_subnet and remote_subnet are mandatory parameters. but now one of them is not provided.';
        LogInfo("local subnet and remote_subnet are mandatory parameters. but now one of them is not provided.");
        return @result;
    }
    ##### check dns server address if use dynamic vpn
    if( (! exists $params{remote_ip}) and (! exists $params{dns_ip} ) ){
        $result[0]='nok';
        $result[1]='dynamic bovpn need dns server ip(dns_ip=>x.x.x.x)';
        LogInfo("dynamic bovpn need dns server ip(dns_ip=>x.x.x.x)");
        return @result;
    }
    @result = _parser_subnet_form($params{remote_subnet});
    if($result[0] eq "ok"){
        $params{remote_subnet} = $result[1];
    }else{
        LogInfo("remote_subnet Error:$result[1]");
        return @result;
    }
    @result = _parser_subnet_form($params{local_subnet});
    if($result[0] eq "ok"){
        $params{local_subnet} = $result[1];
    }else{
        LogInfo("local_subnet Error:$result[1]");
        return @result;
    }
    ##### prepare vpn algorithm param
    my $replace_transform = 0;
    if ((exists $params{method}) || (exists $params{encry})){
        $replace_transform=1;
    }

    $params{gw_name} = $params{gw_name}?$params{gw_name}:'gateway.1';
    $params{tunnel_name} = $params{tunnel_name}?$params{tunnel_name}:'tunnel.1';
    $params{local_if_alias} = $params{local_if_alias}?$params{local_if_alias}:'External';
    $params{pre_shared} = $params{pre_shared}?$params{pre_shared}:'11111111';
    $params{mode} = $params{mode}?$params{mode}:'Aggressive';
    $params{method} = $params{method}?$params{method}:'MD5';
    $params{encry} = $params{encry}?$params{encry}:'AES';
    $params{sa_life} = $params{sa_life}?$params{sa_life}:'8';
    $params{transform_unit} = $params{transform_unit} ? $params{transform_unit} : 'hour';
    $params{encry_length} = $params{encry_length}?$params{encry_length}:'16';
    #######2011-08-18 to support BUG00139  modify by Junkai##############
    $params{nat_traversal} = $params{nat_traversal}?$params{nat_traversal}:'enable';

    #######support ah proposal modify on cli
    #$params{replace_proposal} = $params{replace_proposal} ? $params{replace_proposal}: "";
    
    #######2011-08-05 to support FEAT00133  modify by lizhi##############
    #$yes eq 'yes' means build != 11.4; eq '' means build == 11.4
    if ($yes ne '') {
        $params{key_group} = $params{key_group}?$params{key_group}:'Diffie-Hellman-Group5';
        $params{key_group} =~ s/Diffe-Hellman/Diffie-Hellman/;
    } else {
        $params{key_group} = $params{key_group}?$params{key_group}:'Diffe-Hellman-Group5';
        $params{key_group} =~ s/Diffie-Hellman/Diffe-Hellman/;
    }
    ##########################################################
    $params{phase2_pfs} = $params{phase2_pfs}?$params{phase2_pfs}:'dh-group5';
    $params{phase2_proposal_name} = $params{phase2_proposal_name}?$params{phase2_proposal_name}:'ESP-AES-SHA1';
    
    #######Add by WenbinChen to make bovpn-interop case when config a personal proposal can complemented replace yes.
    if ($params{phase2_proposal_name} eq "ESP-AES-SHA1") {
        $params{replace_proposal} = "";
    
    } else{
        $params{replace_proposal} = $params{replace_proposal} ? $params{replace_proposal}: "replace yes";
    }
    #######Add by WenbinChen end.    
    ###############1. endpoint command under gateway command group ############
    ## for dynamic vpn, need dns server.
    if( exists $params{dns_ip} ){
        @cli_cmd=( "config", 
                   "no ip dns server ".$config->getDnsIP,
                   "ip dns server ".$params{dns_ip},
                   "exit"
                  );
    }else{
        @cli_cmd=(" ");
    }
    @cli_cmd=(
        @cli_cmd,
        "config",
        #"no ip dns server ".$config->getDnsIP; ##for DD
        "policy",
        "no bovpn-tunnel $params{tunnel_name}",
        "no bovpn-gateway $params{gw_name}",
        "bovpn-gateway $params{gw_name}"      
    );
    ##### handle certificate or pre-shared
    if(exists $params{'certificate_id'} and exists $params{'certificate_type'} and exists $params{'certificate_algorithm'} and exists $params{'certificate_string'}) {
        ##add certificate type 2012-09-12 to support add by David Wu##############
        ## generate endpoint command str for certificate.
        if( exists $params{'remote_ip'} ){
            ## used for static bovpn for certificate
            push(@cli_cmd,"endpoint $params{'remote_ip'} $params{remote_ID} $params{local_ID} $params{local_if_alias} certificate $params{certificate_id} $params{certificate_type} $params{certificate_algorithm} $params{certificate_string}");   
         }else{
            ## used for dynamic bovpn for certificate
            push(@cli_cmd,"endpoint dynamic $params{remote_ID} $params{local_ID} $params{local_if_alias} certificate $params{certificate_id} $params{certificate_type} $params{certificate_algorithm} $params{certificate_string}");
         }
    }else{
        ## generate endpoint command str. 
        if( exists $params{'remote_ip'} ){
            ## used for static bovpn
            push(@cli_cmd,"endpoint $params{'remote_ip'} $params{remote_ID} $params{local_ID} $params{local_if_alias} pre-shared $params{pre_shared} ");    
        }else{
            ## used for dynamic bovpn
            push(@cli_cmd,"endpoint dynamic $params{remote_ID} $params{local_ID} $params{local_if_alias} pre-shared $params{pre_shared} ");
        }
    }
    
    ###############2. phase1 command unber gateway command group  ############                 
    push(@cli_cmd,"phase1 mode $params{mode}");
    #######2011-08-18 to support BUG00139  modify by Junkai##############
    if ($params{nat_traversal} eq "disable") {
        push(@cli_cmd, "no phase1 nat-traversal enable");
    } else {
        push(@cli_cmd, "phase1 nat-traversal enable");
    }
    push(@cli_cmd,"phase1 transform $params{method} $params{encry} $params{sa_life} encryp-key-length $params{encry_length} unit $params{transform_unit} $params{key_group}");
    
    if ($replace_transform == 1){
        push(@cli_cmd, "no phase1 transform 1","apply");
    }
    
    ###############3. gateway command under tunnel command group  ############
    push(@cli_cmd,"exit",
                  "bovpn-tunnel $params{tunnel_name}"); 
    $tmpStr="gateway $params{gw_name} $params{local_subnet} $params{remote_subnet}";
    if(exists $params{direction}){
         $tmpStr=$tmpStr." ".$params{direction};
    }
    ##dnat or one_one_nat
    if(exists $params{dnat} and exists $params{one_one_nat}){  
        $result[0]='nok';
        $result[1]="'nat' and 'one_to_one' parameter are exclusive!!!";
        LogInfo("'nat' and 'one_to_one' parameter are exclusive!!!");
        return @result;
    }elsif(exists $params{one_one_nat}){  
        my @temp = _parser_subnet_form($params{one_one_nat});
        $tmpStr= $tmpStr." ".$temp[1]; 
    }elsif(exists $params{dnat}){
        $tmpStr= $tmpStr." dnat ".$params{dnat}; 
    }
    ##enable broadcast over tunnel
    if(exists $params{broadcast_over_tunnel}){
        $tmpStr= $tmpStr." broadcast-over-tunnel ".$params{broadcast_over_tunnel};
    }
    ##finish gateway command
    push(@cli_cmd,$tmpStr);
    
    ###########2011-08-05 to support FEAT00134 modify by lizhi#############
    #          2011-11-19 to support BUG by Junkai, add $params{replace_proposal} #####
    #if disable, do not enable pfs in dut
    ###########2012-06-25 to support BUG00221 modify by shewn##############
    if ($params{replace_proposal} eq 'yes') {
       if ($params{phase2_pfs} eq 'disable') {
          @cli_cmd=(  @cli_cmd,
                   "phase2 proposals $params{phase2_proposal_name} replace $params{replace_proposal}");
       } else {
          @cli_cmd=(  @cli_cmd,
                  "phase2 pfs enable $params{phase2_pfs}",
                  "phase2 proposals $params{phase2_proposal_name} replace $params{replace_proposal}");
       }
    }elsif ($params{replace_proposal} eq 'replace yes') {
       if ($params{phase2_pfs} eq 'disable') {
          @cli_cmd=(  @cli_cmd,
                   "phase2 proposals $params{phase2_proposal_name} $params{replace_proposal}");
       } else {
          @cli_cmd=(  @cli_cmd,
                  "phase2 pfs enable $params{phase2_pfs}",
                  "phase2 proposals $params{phase2_proposal_name} $params{replace_proposal}");
       }
    } else {
       if ($params{phase2_pfs} eq 'disable') {
          @cli_cmd=(  @cli_cmd,
                  "phase2 proposals $params{phase2_proposal_name}");
       } else {
          @cli_cmd=(  @cli_cmd,
                  "phase2 pfs enable $params{phase2_pfs}",
                  "phase2 proposals $params{phase2_proposal_name}");
       }    
    }
    ############################################################
    
    ##enable multi endpoint params and multicast setting .  
    if(exists $params{multicast_endpoint_local} or exists $params{multicast_endpoint_remote} ){
        #must provide both endpoint params:
        if(! (exists $params{multicast_endpoint_local} and exists $params{multicast_endpoint_remote}) ){
            $result[0]='nok';
            $result[1]="'multicast_endpoint_local' and 'multicast_endpoint_remote' parameters must provided at the same time!";
            LogInfo("'multicast_endpoint_local' and 'multicast_endpoint_remote' parameters must provided at the same time!");
            return @result;
        }
        #check multicast setting params
        #must provide all of the three parts.
        if( !(
              exists $params{multicast_origination_ip} and 
              exists $params{multicast_group_ip} and 
              (exists $params{multi_input_intf} or exists $params{multi_output_intf}) ) ){
            $result[0]='nok';
            $result[1]="multicast setting need multicast_origination_ip and multicast_group_ip and multi_input_intf/multi_output_intf parameters!";
            LogInfo("multicast setting need multicast_origination_ip and multicast_group_ip and multi_input_intf/multi_output_intf parameters!");
            return @result; 
        }
        #should provide one of interface type.
        if(exists $params{multi_output_intf} and exists $params{multi_input_intf} ){
            $result[0]='nok';
            $result[1]="Error: multi_input_intf and multi_output_intf can not exist at the same time!";
            LogInfo("Error: multi_input_intf and multi_output_intf can not exist at the same time!");
            return @result; 
        }elsif( exists $params{multi_input_intf} ){
            $params{multi_intf} = "input ".$params{multi_input_intf};
        }elsif(exists $params{multi_output_intf}){
            $params{multi_intf} = "output ".$params{multi_output_intf};
        }
        push(@cli_cmd,"tunnel-endpoints $params{multicast_endpoint_local} $params{multicast_endpoint_remote}");
        push(@cli_cmd,"multicast-settings enable $params{multicast_origination_ip} $params{multicast_group_ip} $params{multi_intf}");
    }## end of multicast param checking.
    
    ###############4.phase2 command under tunnel command group  ############
    @cli_cmd=(@cli_cmd,
                "apply",
                "exit",
                "rule BOVPN-Allow.out",
                "move 1",
                "exit",
                "rule BOVPN-Allow.in",
                "move 1",
                "apply"
             );
    ###############5. connect cli to execute command  ############
    my $result = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ($result eq 'nok'){
        print "XtmCli:configDut_bovpn: get connect_cli nok, return nok...\n";
        return 'nok';
    }    
    my @result_cmd= $self->cmd(@cli_cmd);   
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return @result_cmd;
} ### end of configBovpn

sub configBovpn_vif{
    my $self   = shift;
    my %params = @_;
    my @result;
    my @cli_cmd;
    my $tmpStr;
    ###############0. prepare parameters before compose commands.############
    ##### mandatory params check

    ##### prepare remote gateway ID parameter with different format for dynamic bovpn
    

    #####################################################################################
    if (
        (
                exists $params{remote_ID_ip}
            and exists $params{remote_ID_domain_name}
        )
        or (    exists $params{remote_ID_domain_name}
            and exists $params{remote_ID_user_domain} )
        or (    exists $params{remote_ID_ip}
            and exists $params{remote_ID_user_domain} )
      )
    {
        ## if provide remote_ip, mean estable static bovpn. Remote ID can not be provide more than twice.
        $result[0] = 'nok';
        $result[1] ='ERROR: remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameters can not existed at the same time.';
        LogInfo($result[1]);
        return @result;
    }
    elsif ( exists $params{remote_ID_domain_name} ) {
        $params{remote_ID} =
          "by-domain domain-name $params{remote_ID_domain_name}.wats $yes";
    }
    elsif ( exists $params{remote_ID_user_domain} ) {
        $params{remote_ID} =
          "by-domain user-domain $params{remote_ID_user_domain}.wats";
    }
    elsif ( exists $params{remote_ID_ip} ) {
        $params{remote_ID} = $params{remote_ID_ip};
    }
    else {
        if ( exists $params{'remote_ip'} ) {
            ## if no any form of remote ID provide, but provided remote_ip, use remote_ip as ID.
            $params{remote_ID} = $params{remote_ip};
        }
        else {
            ## if no any form of remote ID and no remote_ip provided, report error.
            $result[0] = 'nok';
            $result[1] = 'dynamic bovpn configre need one of remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameter. But now there no parameters provide.';
            LogInfo($result[1]);
            return @result;
        }
    }
    ##### prepare local gateway ID parameter with different format
    if (
        exists $params{local_ID_ip} and exists $params{local_ID_domain_name}
        or (    exists $params{local_ID_domain_name}
            and exists $params{local_ID_user_domain} )
        or
        ( exists $params{local_ID_ip} and exists $params{local_ID_user_domain} )
      )
    {
        $result[0] = 'nok';
        $result[1] = 'local_ID_ip, local_ID_domain_name and local_ID_user_domain parameters can not existed at the same time.';
        LogInfo($result[1]);
        return @result;
    }
    elsif ( exists $params{local_ID_domain_name} ) {
        $params{local_ID} =
          "by-domain domain-name $params{local_ID_domain_name}.wats";
    }
    elsif ( exists $params{local_ID_user_domain} ) {
        $params{local_ID} =
          "by-domain user-domain $params{local_ID_user_domain}.wats";
    }
    elsif ( exists $params{local_ID_ip} ) {
        $params{local_ID} = $params{local_ID_ip};
    }
    else {
        ## if no any form of local ID provide, use remote gateway ip as ID.
        $result[0] = 'nok';
        $result[1] = 'bovpn configre need one of local_ID_ip, local_ID_domain_name and local_ID_user_domain parameter. But now there no parameters provide.';
        LogInfo($result[1]);
        return @result;
    }

    ##### check dns server address if use dynamic vpn
    if ( ( !exists $params{remote_ip} ) and ( !exists $params{dns_ip} ) ) {
        $result[0] = 'nok';
        $result[1] = 'dynamic bovpn need dns server ip(dns_ip=>x.x.x.x)';
        LogInfo($result[1]);
        return @result;
    }
    
    ##### prepare vpn algorithm param
    my $replace_transform = 0;
    if ( ( exists $params{method} ) || ( exists $params{encry} ) ) {
        $replace_transform = 1;
    }
    my $yes = $params{resolve} ? $params{resolve} : "yes";
    $params{vif_name} = $params{vif_name} ? $params{vif_name} : 'BovpnVif.1';
    #$params{tunnel_name} =
    #  $params{tunnel_name} ? $params{tunnel_name} : 'tunnel.1';
    $params{local_if_alias} =
      $params{local_if_alias} ? $params{local_if_alias} : 'External';
    $params{pre_shared} = $params{pre_shared} ? $params{pre_shared} : '11111111';
    $params{mode}    = $params{mode}    ? $params{mode}    : 'Main';
    $params{method}  = $params{method}  ? $params{method}  : 'SHA1';
    $params{encry}   = $params{encry}   ? $params{encry}   : 'DES-3';
    $params{sa_life} = $params{sa_life} ? $params{sa_life} : '8';
    $params{transform_unit} = $params{transform_unit} ? $params{transform_unit} : 'hour';
    $params{encry_length} =  $params{encry_length} ? $params{encry_length} : '16';
    #######2011-08-18 to support BUG00139  modify by Junkai##############
    $params{nat_traversal} = $params{nat_traversal} ? $params{nat_traversal} : 'enable';

    # to support vif, and interface IP is optional, only required when joining dynamic routing
    #$params{remote_vif_ip} = $params{remote_vif_ip} ? $params{remote_vif_ip} : '';
    #$params{local_vif_ip}  = $params{local_vif_ip}  ? $params{local_vif_ip}  : '';


    #######support ah proposal modify on cli
#$params{replace_proposal} = $params{replace_proposal} ? $params{replace_proposal}: "";
    $params{key_group} =
      $params{key_group} ? $params{key_group} : 'Diffie-Hellman-Group5';
    $params{key_group} =~ s/Diffe-Hellman/Diffie-Hellman/;
    
    ##########################################################
    $params{phase2_pfs} =
      $params{phase2_pfs} ? $params{phase2_pfs} : 'dh-group5';
    $params{phase2_proposal_name} = $params{phase2_proposal_name} ? $params{phase2_proposal_name} : 'ESP-AES-SHA1';

    #######Add by WenbinChen to make bovpn-interop case when config a personal proposal can complemented replace yes.
    if ( $params{phase2_proposal_name} eq "ESP-AES-SHA1" ) {
        $params{replace_proposal} = "";

    }
    else {
        $params{replace_proposal} =
          $params{replace_proposal} ? $params{replace_proposal} : "replace yes";
    }

    my $vpn_route = "";

    if (exists($params{vpn_route})){
        $vpn_route = $params{vpn_route};
    }

    #######Add by WenbinChen end.
    ###############1. endpoint command under gateway command group ############
    ## for dynamic vpn, need dns server.
    if ( exists $params{dns_ip} ) {
        @cli_cmd = (
            "config",
            "no ip dns server " . $config->getDnsIP,
            "ip dns server " . $params{dns_ip}, "exit"
        );
    }
    else {
        @cli_cmd = (" ");
    }
    @cli_cmd = (
        @cli_cmd,
        "config",

        #"no ip dns server ".$config->getDnsIP; ##for DD
        "policy",
        "no bovpn-vif $params{vif_name}",
        "bovpn-vif $params{vif_name}"
    );
    ##### handle certificate or pre-shared
    if (    exists $params{'certificate_id'}
        and exists $params{'certificate_type'}
        and exists $params{'certificate_algorithm'}
        and exists $params{'certificate_string'} )
    {
        ##add certificate type 2012-09-12 to support add by David Wu##############
        ## generate endpoint command str for certificate.
        if ( exists $params{'remote_ip'} ) {
            ## used for static bovpn for certificate
            push( @cli_cmd, "endpoint $params{'remote_ip'} $params{remote_ID} $params{local_ID} $params{local_if_alias} certificate $params{certificate_id} $params{certificate_type} $params{certificate_algorithm} $params{certificate_string}"
            );
        }
        else {
            ## used for dynamic bovpn for certificate
            push( @cli_cmd, "endpoint dynamic $params{remote_ID} $params{local_ID} $params{local_if_alias} certificate $params{certificate_id} $params{certificate_type} $params{certificate_algorithm} $params{certificate_string}"
            );
        }
    }
    else {
        ## generate endpoint command str.
        if ( exists $params{'remote_ip'} ) {
            ## used for static bovpn
            push( @cli_cmd, "endpoint $params{'remote_ip'} $params{remote_ID} $params{local_ID} $params{local_if_alias} pre-shared $params{pre_shared} "
            );
        }
        else {
            ## used for dynamic bovpn
            push( @cli_cmd, "endpoint dynamic $params{remote_ID} $params{local_ID} $params{local_if_alias} pre-shared $params{pre_shared} "
            );
        }
    }

    if ( exists($params{remote_vif_ip}) or exists($params{local_vif_ip}) ){
        if (exists($params{remote_vif_ip}) and exists($params{local_vif_ip})){

            push( @cli_cmd, "virtual-ip $params{local_vif_ip} $params{remote_vif_ip}" );

        }else{
            $result[0] = 'nok';
            $result[1] = 'local_vif_ip and remote_vif_ip should be exists or not exists at same time.';
            LogInfo($result[1]);
            return @result;
        }
    }


    ###############phase1 command unber gateway command group  ############
    if ($params{mode} !~ /^Main$/i){
        push( @cli_cmd, "phase1 mode $params{mode}" );
    }
    
    if ( $params{nat_traversal} eq "disable" ) {
        push( @cli_cmd, "no phase1 nat-traversal enable" );
    }
    

    if ( $replace_transform == 1 ) {
        push( @cli_cmd,
              "phase1 transform $params{method} $params{encry} $params{sa_life} encryp-key-length $params{encry_length} unit $params{transform_unit} $params{key_group}"
        );
        push( @cli_cmd, "no phase1 transform 1" );
    }

    #push( @cli_cmd, "" );

    foreach (@{$vpn_route}){
        if (exists($_->{metric})){
            push( @cli_cmd, "vpn-route $_->{to} $_->{metric}");
            next
        }
        push( @cli_cmd, "vpn-route $_->{to} 1");
    }

    if ( $params{phase2_proposal_name} ne "ESP-AES-SHA1" ) {
        
        if ( $params{replace_proposal} eq 'yes' ) {
            if ( $params{phase2_pfs} eq 'disable' ) {
                @cli_cmd = (
                    @cli_cmd,
                        "phase2 proposals $params{phase2_proposal_name} replace $params{replace_proposal}"
                );
            }
            else {
                @cli_cmd = (
                    @cli_cmd,
                    "phase2 pfs enable $params{phase2_pfs}",
                    "phase2 proposals $params{phase2_proposal_name} replace $params{replace_proposal}"
                );
            }
        }
        elsif ( $params{replace_proposal} eq 'replace yes' ) {
            if ( $params{phase2_pfs} eq 'disable' ) {
                @cli_cmd = (
                    @cli_cmd,
                    "phase2 proposals $params{phase2_proposal_name} $params{replace_proposal}"
                );
            }
            else {
                @cli_cmd = (
                    @cli_cmd,
                    "phase2 pfs enable $params{phase2_pfs}",
                    "phase2 proposals $params{phase2_proposal_name} $params{replace_proposal}"
                );
            }
        }
        else {
            if ( $params{phase2_pfs} eq 'disable' ) {
                @cli_cmd =
                  ( @cli_cmd, "phase2 proposals $params{phase2_proposal_name}" );
            }
            else {
                @cli_cmd = (
                    @cli_cmd,
                    "phase2 pfs enable $params{phase2_pfs}",
                    "phase2 proposals $params{phase2_proposal_name}"
                );
            }
        }
    }
    ############################################################

    ##enable multi endpoint params and multicast setting .
    if (   exists $params{multicast_enable} and $params{multicast_enable} eq "enable")
    {

        if (
            !(
                    exists $params{multicast_origination_ip}
                and exists $params{multicast_group_ip}
                and (  exists $params{multi_input_intf}
                    or exists $params{multi_output_intf} )
            )
          )
        {
            $result[0] = 'nok';
            $result[1] =
                    "multicast setting need multicast_origination_ip and multicast_group_ip and multi_input_intf/multi_output_intf parameters!";
            LogInfo($result[1]);
            return @result;
        }

        #should provide one of interface type.
        if (    exists $params{multi_output_intf}
            and exists $params{multi_input_intf} )
        {
            $result[0] = 'nok';
            $result[1] =
                    "Error: multi_input_intf and multi_output_intf can not exist at the same time!";
            LogInfo($result[1]);
            return @result;
        }
        elsif ( exists $params{multi_input_intf} ) {
            $params{multi_intf} = "input " . $params{multi_input_intf};
        }
        elsif ( exists $params{multi_output_intf} ) {
            $params{multi_intf} = "output " . $params{multi_output_intf};
        }

        push( @cli_cmd,
                "multicast-settings enable $params{multicast_origination_ip} $params{multicast_group_ip} $params{multi_intf}"
        );
    } ## end of multicast param checking.

    ###############4.phase2 command under tunnel command group  ############
    @cli_cmd = (
        @cli_cmd, "apply", "exit"
    );

    ##############5. connect cli to execute command  ############
    my $result        = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ( $result eq 'nok' ) {
        print "XtmCli:bovpn_vif: get connect_cli nok, return nok...\n";
        return 'nok';
    }
    my @result_cmd = $self->cmd(@cli_cmd);
    if ($if_new_handle) {
        $self->disconnect;
    }
    else {
        $self->switch_to_cli;
    }
    return @result_cmd;
}    ### end of configBovpn_vif

# /*!
#   @function edit_bovpngateway
#   @param(gateway=>'abcd',endpoints=>[{remote_ID_ip=>'x.x.x.x',remote_ID_domain_name=>'abc.com'|remote_ID_user_domain=>'cba.com', local_ID_ip=>'x.x.x.x',local_ID_domain_name=>'abc.com'|local_ID_user_domain=>'cba.com',local_if_alias=>'External',direction=>'bi-direction|inbound|outbound'},{remote_ID_ip=>'x.x.x.x',remote_ID_domain_name=>'abc.com'|remote_ID_user_domain=>'cba.com', local_ID_ip=>'x.x.x.x',local_ID_domain_name=>'abc.com'|local_ID_user_domain=>'cba.com',local_if_alias=>'External',direction=>'bi-direction|inbound|outbound'}...]
#   @return \@result  $result[0]:ok/nok. ok means add all endpoints successfully, nok means endpoint add failed.
#                     $result[1]:the output info of cmd(s)
#   @discussion 
#   <h2>Description: </h2>             
#   <pre>This method is aimed to edit an exited bovpnageway to add multiple endpoints to gateway. This function need at least 3 parameters:gateway, one endpoint pair include remote_ip( or remote_ID_domain_name/remote_ID_user_domain/remote_ID_ip), local_ID(provide one form of local_ID_ip/local_ID_domain_name/local_ID_user_domain).</pre>
#   <pre>If provide remote_ip, mean establish static bovpn, otherwise, establish dynamic bovpn. Default value will be first value above in param example. If only provide basic params, and vpn tunnel will be established use default params. If certain parameter need to be specified, just provide the parameter when invoke the function. </pre>
#   <h2>Example:</h2>
#   <pre>my $dut= new XTM('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#
#   <pre>one endpoint example:
#   <pre>my \@result=$dut->cli->configBovpn(gw_name=>"bovpn_gateway",remote_ip=>'192.168.77.33', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');
#        my \@result=$dut->cli->edit_bovpngateway(gateway_name=>"bovpn_gateway",endpoints=>[{remote_ip=>'1.1.1.1', local_ID_ip=>'2.2.2.2'}])
#   <pre>multiple endpoints example:
#   <pre>my \@result=$dut->cli->edit_bovpngateway( gateway_name=>"bovpn_gateway",endpoints=>[{remote_ip=>'3.3.3.3', local_ID_ip=>'4.4.4.4'},{remote_ID_domain_name=>'remote.com', local_ID_domain_name=>'local.com'}]);</pre>
#
#
# */
sub edit_bovpngateway{
    my $self = shift;
    my %all_para = @_;
    my @cli_cmd;
    my @result_final;
    my @result;
    my @addendpoint_result;
    my $gateway_name;
    my $args_array;
    if(exists $all_para{gateway}){
        $gateway_name = $all_para{gateway};
    }else{
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpngateway: lack of parameter gateway_name.\n";
        return @result_final;        
    }
    if(exists $all_para{endpoints}){
        $args_array = $all_para{endpoints};
    }else{
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpngateway: lack of parameter endpoints.\n";
        return @result_final;        
    }
    
    

    #if the gateway_name has already exited.
    my $result_connect = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ($result_connect eq 'nok'){
        LogInfo("XtmCli:edit_bovpngateway: get connect_cli nok, return nok...\n");
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpngateway: get connect_cli nok, return nok...\n";
        return @result_final;
    }    
    my @result_showbovpn= $self->cmd('show bovpn-gateway');
    
    if(($result_showbovpn[0] eq 'nok') or ($result_showbovpn[1] !~ m/$gateway_name/)){
        LogInfo("XtmCli:edit_bovpngateway: no Bovpn gateway $gateway_name exist, please create it by API configBovpn.\n");
        $result_final[0] = 'nok';
        $result_final[1] = "XtmCli:edit_bovpngateway: no Bovpn gateway $gateway_name exist, please create it by API configBovpn.\n";
        return @result_final;
    }

    ##############################prepare commond ######################
    @cli_cmd = (
        "config",
        "policy",
        "bovpn-gateway $gateway_name",      
    );
    
    #devide the endpoints parameters
    foreach my $params (@$args_array) {
        @addendpoint_result = add_endpoints($params);
        if ($addendpoint_result[0] eq "ok"){
            push (@cli_cmd, $addendpoint_result[1]);
        }
        else{
            @result_final = @addendpoint_result;
            return @result_final;
        }           
    }
    push(@cli_cmd, 'apply');
    
    ##############################execute commond ######################   
    my @result_cmd= $self->cmd(@cli_cmd);   
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    if(($result_cmd[0] eq "nok") or ($result_cmd[1] =~ m/Warning|Error|Invalid/)){
        LogInfo("\nFailed added endpoint\n");
        #LogInfo($result_cmd[1]);
        $result_final[0] = "nok";
        $result_final[1] = $result_cmd[1];      
    }
    else{
        @result_final = @result_cmd; 
    }
    return @result_final;
}
#end of function edit_bovpngateway


###########################################
### internal function for edit_bovpngateway    ##
###########################################
# add endpoints to bovpn gateway. 
#return a array, result[0] is ok/nok, ok means the parameter form are legal,otherwise, parameter is illegal 
# result[1] is illegal parameters info or add endpoint commond.
sub add_endpoints{
    my $params = shift;
    my @result;
    $result[0] = 'ok';
    my $cli_cmd;
    my $yes = '';#if version != 11.4,need yes when config doamin_name
    
    my $sys_info = $self->obtain_systeminfo();
    #$params->{resolve} = $params->{resolve} ? $params->{resolve}: $yes;
    if ($sys_info ne 'nok') {
        if ($sys_info =~ m/version\s+:\s+11\.4/) {
            #$yes = 'yes';
        } else {
              $yes = $params->{resolve} ? $params->{resolve}: "yes";
        }
    }

    
 ########################prepare remote parameters#############################################################
    if( (exists $params->{remote_ID_ip} and exists $params->{remote_ID_domain_name}) or
        (exists $params->{remote_ID_domain_name} and exists $params->{remote_ID_user_domain}) or
        (exists $params->{remote_ID_ip} and exists $params->{remote_ID_user_domain}) ){
        ## if provide remote_ip, mean estable static bovpn. Remote ID can not be provide more than twice.
        $result[0]='nok';
        $result[1]='ERROR: remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameters can not existed at the same time.';
        LogInfo("ERROR: remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameters can not existed at the same time.");
        return @result;
    }elsif( exists $params->{remote_ID_domain_name} ){
        $params->{remote_ID}="by-domain domain-name $params->{remote_ID_domain_name}.wats $yes";
    }elsif( exists $params->{remote_ID_user_domain} ){
        $params->{remote_ID}="by-domain user-domain $params->{remote_ID_user_domain}.wats";
    }elsif( exists $params->{remote_ID_ip} ){
        $params->{remote_ID}=$params->{remote_ID_ip};
    }else{
        if( exists $params->{'remote_ip'} ){
            ## if no any form of remote ID provide, but provided remote_ip, use remote_ip as ID.
            $params->{remote_ID}=$params->{remote_ip};
        }else{
            ## if no any form of remote ID and no remote_ip provided, report error.
            $result[0]='nok';
            $result[1]='endpoint configre need one of remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameter. But now there no parameters provide.';
            LogInfo("endpoint configre need one of remote_ID_ip, remote_ID_domain_name and remote_ID_user_domain parameter. But now there no parameters provide.");
            return @result;
        }
    }
    ##### prepare local gateway ID parameter with different format##################################################
    if(exists $params->{local_ID_ip} and exists $params->{local_ID_domain_name} or
      (exists $params->{local_ID_domain_name} and exists $params->{local_ID_user_domain}) or
      (exists $params->{local_ID_ip} and exists $params->{local_ID_user_domain}) ){
        $result[0]='nok';
        $result[1]='local_ID_ip, local_ID_domain_name and local_ID_user_domain parameters can not existed at the same time.';
        LogInfo("local_ID_ip, local_ID_domain_name and local_ID_user_domain parameters can not existed at the same time.");
        return @result;
    }elsif( exists $params->{local_ID_domain_name} ){
        $params->{local_ID}="by-domain domain-name $params->{local_ID_domain_name}.wats";
    }elsif( exists $params->{local_ID_user_domain} ){
        $params->{local_ID}="by-domain user-domain $params->{local_ID_user_domain}.wats";
    }elsif( exists $params->{local_ID_ip} ){
        $params->{local_ID}=$params->{local_ID_ip};
    }else{
        ## if no any form of local ID provide, use remote gateway ip as ID.
        $result[0]='nok';
        $result[1]='endpoint configre need one of local_ID_ip, local_ID_domain_name and local_ID_user_domain parameter. But now there no parameters provide.';
        LogInfo("endpoint configre need one of local_ID_ip, local_ID_domain_name and local_ID_user_domain parameter. But now there no parameters provide.");
        return @result;
    }
    
    $params->{local_if_alias} = $params->{local_if_alias}?$params->{local_if_alias}:'External';
#######################################prepare the command line###############################################################333
    
    if( exists $params->{'remote_ip'} ){
        ## used for static bovpn
        $cli_cmd = "endpoint $params->{'remote_ip'} $params->{remote_ID} $params->{local_ID} $params->{local_if_alias}";
    }else{
         ## used for dynamic bovpn
        $cli_cmd = "endpoint dynamic $params->{remote_ID} $params->{local_ID} $params->{local_if_alias}";
    }
    $result[1] = $cli_cmd;
    return @result;
}

#end of function add_endpoints

# /*!
#   @function edit_bovpntunnel
#   @param(tunnel_name=>'abcd',tunnels=>[{remote_subnet=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x', local_subnet=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x',direction=>'bi-direction|inbound|outbound',[dnat=>x.x.x.x],[one_one_nat=>'x.x.x.x/x|x.x.x.x-x.x.x.x|x.x.x.x'],broadcast_over_tunnel=>'enable'},{}...]
#   @return \@result  $result[0]:ok/nok. ok means add all tunnels successfully, nok means tunnels add failed.
#                     $result[1]:the output info of cmd(s)
#   @discussion 
#   <h2>Description: </h2>             
#   <pre>This method is aimed to edit an exited tunnel to add multiple tunnels to tunnel. This function need at least 3 parameters:gateway, one endpoint pair include remote_subnet(three different forms), local_subnet(three different forms).</pre>
#   <h2>Example:</h2>
#   <pre>my $dut= new XTM('HostIP'=>'192.168.1.1', SNIP=>'10.134.3.3',SNPort=>'5001');
#
#   <pre>one tunnel example:
#   <pre>my \@result=$dut->cli->configBovpn(tunnel_name=>"bovpn_tunnel",remote_ip=>'192.168.77.33', local_ID_ip=>'2.3.3.4', local_subnet=>'172.16.1.0/24', remote_subnet=>'10.23.1.0/24');
#        my \@result=$dut->cli->edit_bovpntunnel(tunnel_name=>"bovpn_tunnel",tunnels=>[{remote_subnet=>'1.1.1.1', local_subnet=>'2.2.2.2'}])
#   <pre>multiple tunnels example
#   <pre>my \@result=$dut->cli->edit_bovpntunnel(tunnel_name=>"bovpn_tunnel",tunnels=>[{remote_subnet=>'3.3.3.0/24', local_subnet=>'4.4.4.0/24', direction=>'bi-direction'},{remote_subnet=>'1.1.1.1', local_subnet=>'2.2.2.2'}]);</pre>
#
#
# */
sub edit_bovpntunnel{
  
    my $self = shift;
    my %all_para = @_;
    my $tunnel_name;
    my $args_array;
    my @result_final;
    my @result;
    my @cli_cmd;
    #check if parameters right
    if(exists $all_para{tunnel_name} ){
        $tunnel_name = $all_para{tunnel_name};
    }else{
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpntunnel: lack of parameter tunnel_name.\n";
        return @result_final;
    }
    if(exists $all_para{tunnels} ){
        $args_array = $all_para{tunnels};
    }else{
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpntunnel: lack of parameter tunnels.\n";
        return @result_final;
    }   

    #if the tunnel_name has already exited.
    my $result_connect = $self->connect_cli;
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    if ($result_connect eq 'nok'){
        LogInfo("XtmCli:edit_bovpntunnel: get connect_cli nok, return nok...\n");
        $result_final[0] = "nok";
        $result_final[1] = "XtmCli:edit_bovpntunnel: get connect_cli nok, return nok...\n";
        return @result_final;
    }  
    my @result_showbovpntunnal= $self->cmd('show bovpn-tunnel');

    if(($result_showbovpntunnal[0] eq 'nok') or ($result_showbovpntunnal[1] !~ m/$tunnel_name/)){
        LogInfo("XtmCli:edit_bovpntunnel: no Bovpn tunnel $tunnel_name exist, please create it by API configBovpn.\n");
        $result_final[0] = 'nok';
        $result_final[1] = "XtmCli:edit_bovpntunnel: no Bovpn tunnel $tunnel_name exist, please create it by API configBovpn.\n";
        return @result_final;
    }   

    #if the tunnel_name has already exited.
    @cli_cmd = (
        "config",
        "policy",
        "bovpn-tunnel $tunnel_name",      
    );
    
    #devide the tunnels parameters
    foreach my $params (@$args_array) {
        my @self_re = add_tunnel($params);
        if ($self_re[0] eq 'ok'){
            push(@cli_cmd, $self_re[1]);
        }else{
            @result_final = @self_re;
            return @result_final;
        }   
    }
    push(@cli_cmd, 'apply');

    ##############################execute commond ######################   
    my @result_cmd= $self->cmd(@cli_cmd);   
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    if(($result_cmd[0] eq "nok") or ($result_cmd[1] =~ m/Warning|Error|Invalid/)){
        LogInfo("\nFailed added tunnel\n");
        #LogInfo($result_cmd[1]);
        $result_final[0] = "nok";
        $result_final[1] = $result_cmd[1];      
    }
    else{
        @result_final = @result_cmd; 
    }
    return @result_final;   
    
}

    #if the parameter number is odd, return nok. 
    
###########################################
### internal function for edit_bovpntunnel##
###########################################
# add_tunnels to bovpn 
#return a array, result[0] is ok/nok, ok means the parameter form is legal,otherwise, parameter is illegal 
# result[1] is illegal parameters info or add_tunnel commond.
sub add_tunnel{
    my $params = shift;
    my @result;
    my $tmpStr;
    $result[0] = "ok";

    
 ########################prepare remote parameters#############################################################
    ##### prepare local_subnet/remote_subnet parameter with different format    
    if(! (exists $params->{remote_subnet} and exists $params->{local_subnet}) ){
        $result[0]='nok';
        $result[1]='local_subnet and remote_subnet are mandatory parameters. but now one of them is not provided.';
        LogInfo("local subnet and remote_subnet are mandatory parameters. but now one of them is not provided.");
        return @result;
    }
    
    @result = _parser_subnet_form($params->{remote_subnet});
    if($result[0] eq "ok"){
        $params->{remote_subnet} = $result[1];
    }else{
        LogInfo("remote_subnet Error:$result[1]");
        return @result;
    }
    @result = _parser_subnet_form($params->{local_subnet});
    if($result[0] eq "ok"){
        $params->{local_subnet} = $result[1];
    }else{
        LogInfo("local_subnet Error:$result[1]");
        return @result;
    }   
#######################################prepare the command line#################################################
    $tmpStr="address-pair $params->{local_subnet} $params->{remote_subnet}";
    if(exists $params->{direction}){
         $tmpStr=$tmpStr." ".$params->{direction};
    }
    ##dnat or one_one_nat
    if(exists $params->{dnat} and exists $params->{one_one_nat}){  
        $result[0]='nok';
        $result[1]="'nat' and 'one_to_one' parameter are exclusive!!!";
        LogInfo("'nat' and 'one_to_one' parameter are exclusive!!!");
        return @result;
    }elsif(exists $params->{one_one_nat}){  
        my @temp = _parser_subnet_form($params->{one_one_nat});
        $tmpStr= $tmpStr." ".$temp[1]; 
    }elsif(exists $params->{dnat}){
        $tmpStr= $tmpStr." dnat ".$params->{dnat}; 
    }
    ##enable broadcast over tunnel
    if(exists $params->{broadcast_over_tunnel}){
        $tmpStr= $tmpStr." broadcast-over-tunnel ".$params->{broadcast_over_tunnel};
    }

    $result[1] = $tmpStr;
    return @result;

}



# /*!
#    @function create_p2proposal  
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value of p2 proposal you want create.
#    @return return a list: $return[0]: 'ok' or 'nok'
#                           $return[1]: output of creating proposals.
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Create phase 2 proposals</pre>
#    <h2>Example:</h2>
#    <pre> 
#          my $dut1 = new XTM(eth1=>$dut1_mgmt_ip,SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);
#          my @res = $dut1->cli->create_p2proposal({
#                             name => "proposal100",
#                             type => "esp",
#                             encryption => "des",
#                             authentication => "sha1"
#                             });
#         if ($res[0] eq 'nok') {
#             exit 99;
#         }
#          @res = $dut1->cli->create_p2proposal({
#                             name => "proposal.1010",
#                             type => "ah",
#                             encryption => "none",
#                             authentication => "md5"
#                             });
#         if ($res[0] eq 'nok') {
#             exit 99;
#         }
#    </pre>
# */

sub create_p2proposal {
    my $self = shift;
    my @hargs_array = @_;
    
    my @cmd = ();
    push(@cmd,"config",
              "policy");
    
    my @proposal_list = ();
    
    ######  construct the command list ########
    foreach my $hargs (@hargs_array) {
        # parse the hash, if there is no user set parameter, set to default
        $hargs->{name} = $hargs->{name} ? $hargs->{name} : "proposal.1";
        $hargs->{type} = $hargs->{type} ? $hargs->{type} : "esp";
        $hargs->{sa_lifetime} = $hargs->{sa_lifetime} ? $hargs->{sa_lifetime} : 8*60;
        $hargs->{sa_lifesize} = $hargs->{sa_kilobytes} ? $hargs->{sa_kilobytes} : 128000;
        $hargs->{encryption} = $hargs->{encryption} ? $hargs->{encryption} : "des";
        $hargs->{authentication} = $hargs->{authentication} ? $hargs->{authentication} : "sha1";
        
        push(@proposal_list, $hargs->{name});
        
        # esp and ah proposal has different commands
        if ($hargs->{type} eq "esp") {
            push(@cmd,
         "proposal p2 $hargs->{name} $hargs->{type} transform $hargs->{sa_lifetime} $hargs->{sa_lifesize} $hargs->{encryption} $hargs->{authentication}");
        }elsif ($hargs->{type} eq "ah") {
            push(@cmd,
         "proposal p2 $hargs->{name} $hargs->{type} transform $hargs->{sa_lifetime} $hargs->{sa_lifesize} $hargs->{authentication}");
        }
        push(@cmd,"apply"); 
    }
    
    push(@cmd,"exit",
              "exit");
              
    # execute the commands
    
    my $result = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ($result eq 'nok'){
        LogInfo ("create_p2proposal: get connect_cli nok, return nok...\n");
        return 'nok';
    }
    # send command to cli
    my @result_cmd= $self->cmd(@cmd);
    
    # check whether execution is successful actually.
    
    foreach (@proposal_list) {
       my @ret = $self->cmd("show proposal p2");
         if ($ret[1] !~ /$_/i) {
          LogInfo("create proposal failed: \"$_\".");
          $result_cmd[0] = 'nok';
          last;
         } 
    }
    
    
    # release.....
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return @result_cmd;
}

# /*!
#    @function delete_p2proposal  
#    @param a list of proposal name [required] 
#    @return return a list: $return[0]: 'ok' or 'nok'
#                           $return[1]: output of deleting proposals.
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Deleting phase 2 proposals</pre>
#    <h2>Example:</h2>
#    <pre> 
#          my $dut1 = new XTM(eth1=>$dut1_mgmt_ip,SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);
#          my @res = $dut2->cli->delete_p2proposal($p2_proposal);
#         if ($res[0] eq 'nok') {
#             exit 99;
#         }
#    </pre>
# */

sub delete_p2proposal {
    my $self = shift;
    my @args = @_;
    
    my @cmd = ();
    push(@cmd,"config",
              "policy");
    
    ######  construct the command list ########
    foreach (@args) {
        # parse the hash, if there is no user set parameter, set to default
        push(@cmd,"no proposal p2 $_");
        push(@cmd,"apply"); 
    }
    
    push(@cmd,"exit",
              "exit");
              
    # execute the commands
    
    my $result = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ($result eq 'nok'){
        LogInfo ("create_p2proposal: get connect_cli nok, return nok...\n");
        return 'nok';
    }
    # send command to cli
    my @result_cmd= $self->cmd(@cmd);
    
    # check whether execution is successful actually.
    foreach (@args) {
        my @ret = $self->cmd("show proposal p2");
            if ($ret[1] =~ /$_/i) {
            LogInfo("delete proposal failed.");
            $result_cmd[0] = 'nok';
            last;
        }
    }
    
    # release.....
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return @result_cmd;
}

# /*!
#    @function clear_bovpn  
#    @param [NONE] 
#    @return return a list: $return[0]: 'ok' or 'nok'
#                           $return[1]: output of deleting gateway and tunnel.
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Clear bovpn tunnels and gateways on firebox</pre>
#    <h2>Example:</h2>
#    <pre> 
#          my $dut1 = new XTM(eth1=>$dut1_mgmt_ip,SNIP=>$dut1_sn_ip,SNPort=>$dut1_sn_port);
#          my @res = $dut1->cli->clear_bovpn;
#          if ($res[0] eq 'nok') {
#             exit 99;
#          }
#    </pre>
# */
sub clear_bovpn{
    my $self = shift;
    
    my $result = $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->switch_to_cli;
    if ($result eq 'nok'){
        LogInfo ("clear_bovpn: get connect_cli nok, return nok...\n");
        return 'nok';
    }
    # send command to cli
    my @ret = $self->cmd("show bovpn-tunnel");
    my @tunnel_list = ();
    my @gateway_list = ();
    
    my @cmd_list = ();
    
    # get all bovpn tunnel and gateway, push them to tunnel_list and gateway_list.
    foreach (split("\n",$ret[1])){
        if ($_ =~ /([A-Za-z0-9.-_]+)\s+\:\s?([A-Za-z0-9.-_]+)/){
            #push(@output, $_);
            push(@tunnel_list, $1);
            push(@gateway_list, $2);
        }
    }
    
    push(@cmd_list, "configure");
    push(@cmd_list, "policy");
    
    # deal with each tunnel...
    foreach (@tunnel_list){
        push(@cmd_list, "no bovpn-tunnel $_");
    }
    #deal with each gateway...
    foreach (@gateway_list){
        push(@cmd_list, "no bovpn-gateway $_");
    }
    push(@cmd_list, "apply");
    push(@cmd_list, "exit");
    push(@cmd_list, "exit");
    #
    #print "-"x10;
    #foreach (@cmd_list){
    #    print "new: $_\n";
    #}
    @ret = $self->cmd(@cmd_list);
    
    # see if the tunnel and gateway deleted actually.
    my @verify = $self->cmd("show bovpn-tunnel");
    foreach (@tunnel_list){
        if ($verify[1] =~ /$_/){
            $ret[0] = 'nok';
            last;
        }
    }
    @verify = $self->cmd("show bovpn-gateway");
    foreach (@gateway_list){
        if ($verify[1] =~ /$_/){
            $ret[0] = 'nok';
            last;
        }
    }
    # release.....
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return @ret;
}


###########################################
### internal function for configBovpn    ##
###########################################
# convert three form of subnet. 
#return a array, result[0] is ok/nok, ok means the parameter form are legal, otherwise, parameter is illegal.
# result[1] is partial command:'host-ip x.x.x.x', 'network-ip x.x.x.x/x' or 'range-ip x.x.x.x x.x.x.x'
sub _parser_subnet_form{
    my ($str)=@_;
    my @tmp;
    my @result;
    if( $str =~ /\// ){
        @tmp=split( /\//,$str );
        if(scalar(@tmp)!= 2){
            $result[0] = "nok";
            $result[1] = "error form of subnet:$str. Should be x.x.x.x/x";
            return @result;
        }
        $result[0] = "ok";
        $result[1] = "network-ip $str";
        return @result;
    }elsif( $str=~ /-/ ){
        @tmp=split( /-/,$str );
        if(scalar(@tmp)!= 2){
            $result[0] = "nok";
            $result[1] = "error form of subnet:$str. Should be x.x.x.x-x.x.x.x";
            return @result;
        }
        $result[0] = "ok";
        $result[1] = "range-ip $tmp[0] $tmp[1]";
        return @result;
    }elsif($str=~/\d+.\d+.\d+.\d+/){
        $result[0] = "ok";
        $result[1] = "host-ip $str";
        return @result;
    }elsif($str =~ /any/i){
        $result[0] = "ok";
        $result[1] = "Any";
        return @result;
    }else{
        $result[0] = "nok";
        $result[1] = "error form of subnet:$str. Should be x.x.x.x";
        return @result;
    }
} ##end of _parser_subnet_form

sub LogInfo {
    my $string = shift;
    print "\n====\n",__PACKAGE__,": $string","\n====\n";
}

# /*!
#    @function get certificate request form dut.
#    @param purpose=>'purpose' - the certificate type, should be proxy or ssl-vpn or web or cli.
#    @param name=>'name' -  the name of certificate.
#    @algorithm=>'algorithm' - the encryption type, should be dsa or rsa.
#    @keylength=>'keylength' - the encryption length, should be 1024 or 2048.
#    @IPaddr=>'IPaddr' - the IP address for vpn certificate.
#    @return \@result  $result[0]:ok/nok. $result[1]: certificate request/produce the certificate failed.
#    @discussion
#   <h2>Description: </h2> 
#   <pre>cert request
#        Config a DUT/XTM to get certificate request.  If result ok, means the create ceriticate request ok.</pre>
#   <h2>Example:</h2>
#   <pre>my \@result = cert_request(purpose=>'proxy', name=>'test', algorithm=>'dsa', keylength=>'1024', keyusage=>'both', Email=>'dwu\@watchguard.com', IPaddr=>'192.168.4.2');</pre>
#   <pre>my \@result = cert_request(purpose=>'proxy', name=>'test', algorithm=>'dsa', keylength=>'1024', keyusage=>'both', Email=>'dwu\@watchguard.com');</pre>
# */
sub cert_request() {
    my $self = shift;
    my %params = @_;
    my @result_cmd;
    my $IPlist;
    my @result_req;
    my $if_new_handle = $self->if_new_handle;
    if( ! ( (exists $params{'purpose'} ) && (exists $params{'name'} )&& (exists $params{'algorithm'} )&& (exists $params{'keylength'} ) && (exists $params{'keyusage'} ) && (exists $params{'Email'} ) || (exists $params{'IPaddr'} ))) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters for certificate request!";
        print("Error parameters for certificate request!");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }

    if(exists $params{'IPaddr'}) 
    {
        $IPlist = join("\=","ip_addr","$params{'IPaddr'}");
    }else{
        $IPlist = ' ';
    }
    #produce certificate request
    print("Get the FireBox certificate request.\n");

    if($if_new_handle){
        $self->connect_shell;
    }else{
        $self->connect_shell;
        $if_new_handle = $self->if_new_handle;
    }

    @result_req = $self->cmd("wgcmd action /certificate/req purpose=$params{'purpose'} name=$params{'name'} algorithm=$params{'algorithm'} key_length=$params{'keylength'} key_usage=$params{'keyusage'} ext_alt_name=email:$params{'Email'} subject=C=CN,\\ ST=SC,\\ L=CD,\\ O=WG,\\ CN=WatchGuard $IPlist");

    if ($result_req[0] =~ m/.*ok.*/ and $result_req[1] =~ m/BEGIN\sCERTIFICATE\sREQUEST/) {
        print("produce the certificate success.\n");
        #filter certificate request info
        my @result_split1 = split('-----BEGIN CERTIFICATE REQUEST-----',$result_req[1]);
        my @result_split2 = split('-----END CERTIFICATE REQUEST-----',$result_split1[1]);
        my $cert_req = join("\r","-----BEGIN CERTIFICATE REQUEST-----","$result_split2[0]","-----END CERTIFICATE REQUEST-----\n");
        $result_cmd[0] ='ok';
        $result_cmd[1] ="$cert_req";    
    } else {
        print("produce the certificate failed.\n");
        $result_cmd[0] ='nok';
        $result_cmd[1] ="produce the certificate failed.";
    }
    
    $self->disconnect();
    return @result_cmd;
}

# /*!
#    @function get vpn certificate request form dut.
#    @algorithm=>'algorithm' - the encryption type, should be dsa or rsa.
#    @keylength=>'keylength' - the encryption length, should be 1024 or 2048.
#    @IPaddr=>'IPaddr' - the IP address for vpn certificate.
#    @DNS=>'DNS Ipaddr' - the IP address of DNS.
#    @return \@result  $result[0]:ok/nok. $result[1]: certificate request/produce the vpn certificate failed.
#    @discussion
#   <h2>Description: </h2> 
#   <pre>cert request
#        Config a DUT/XTM to get vpn certificate request.  If result ok, means the create ceriticate request ok.</pre>
#   <h2>Example:</h2>
#   <pre>my \@result = cert_request(purpose=>'proxy', name=>'test', algorithm=>'rsa', keylength=>'1024', keyusage=>'both', Email=>'dwu\@watchguard.com', IPaddr=>'192.168.4.2');</pre>
#   <pre>my \@result = cert_request(purpose=>'proxy', name=>'test', algorithm=>'rsa', keylength=>'1024', keyusage=>'both', Email=>'dwu\@watchguard.com');</pre>
# */
sub vpncert_request() {
    my $self = shift;
    my %params = @_;
    my @result_cmd;
    my $IPlist;
    my @result_req;
    my $if_new_handle = $self->if_new_handle;
    if( ! ( (exists $params{'algorithm'} )&& (exists $params{'keylength'} ) && (exists $params{'keyusage'} ) && (exists $params{'Email'} ) && (exists $params{'DNS'} ) && (exists $params{'IPaddr'} ))) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters for certificate request!";
        print("Error parameters for certificate request!");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }

    if(exists $params{'IPaddr'}) 
    {
        $IPlist = join("\=","ip_addr","$params{'IPaddr'}");
    }else{
        $IPlist = ' ';
    }
    #produce certificate request
    print("Get the FireBox certificate request.\n");
    
    $self->connect_shell;
    if($params{'keyusage'} =~ m/rsa/i){

        @result_req = $self->cmd("wgcmd action /certificate/req gen_cert_req=1 algorithm=$params{'algorithm'} key_length=$params{'keylength'} key_usage=$params{'keyusage'} ext_alt_name=email:$params{'Email'} subject=C=CN,\\ ST=SC,\\ L=CD,\\ O=WG,\\ CN=WatchGuard dvcp=1 dns_name=$params{'DNS'} $IPlist");

    } else {

        @result_req = $self->cmd("wgcmd action /certificate/req gen_cert_req=1 algorithm=$params{'algorithm'} key_length=$params{'keylength'} key_usage=$params{'keyusage'} ext_alt_name=email:$params{'Email'} subject=C=CN,\\ ST=SC,\\ L=CD,\\ O=WG,\\ CN=WatchGuard dvcp=0 rfc_name=test dns_name=$params{'DNS'} $IPlist");

    }
    
    if ($result_req[0] =~ m/.*ok.*/ and $result_req[1] =~ m/BEGIN\sCERTIFICATE\sREQUEST/) {
        print("produce the vpn certificate reauest success.\n");
        #filter certificate request info
        my @result_split1 = split('-----BEGIN CERTIFICATE REQUEST-----',$result_req[1]);
        my @result_split2 = split('-----END CERTIFICATE REQUEST-----',$result_split1[1]);
        my $cert_req = join("\r","-----BEGIN CERTIFICATE REQUEST-----","$result_split2[0]","-----END CERTIFICATE REQUEST-----\n");
        $result_cmd[0] ='ok';
        $result_cmd[1] ="$cert_req";    
    } else {
        print("produce the vpn certificate reauest failed.\n");
        $result_cmd[0] ='nok';
        $result_cmd[1] ="produce the vpn certificate request failed.";
    }
    
    $self->disconnect();
    return @result_cmd;
}

# /*!
#    @function delete the certic
#    @param purpose_name=>'purpose_name' - certificate type 
#    @param name_id=>'name_id' - name of certificate or certificate ID(vpn only)
#    @return \@result  $result[0]:ok/nok. the certificate will be delete in DUT
#    @discussion 
#   <h2>Description: </h2> 
#   <pre>cert request
#        Config a DUT/XTM to import certificate.  If result ok, means the ceriticate already import into DUT.</pre>
#   <h2>Example:</h2>
#   <pre>my \@result = del_cert(purpose_name=>'proxy', name_id=>'test');</pre>
#   <pre>my \@result = del_cert(purpose_name=>'proxy', name_id=>'598');</pre>
# */
sub del_cert() {
    my $self = shift;
    my @result_del;
    my @result_cmd;
    my @result_date;
    my %params = @_;
    my $if_new_handle = $self->if_new_handle;
    if( ! ( (exists $params{'purpose_name'} ) && (exists $params{'name_id'} ))) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters to delete certificate!";
        print("Error parameters for import certificate!");
        if($if_new_handle){
            $self->disconnect;
            $self->check_system_time();
        }else{
            $self->switch_to_cli;
            $self->check_system_time();
        }
        return @result_cmd;
    }

    if($if_new_handle){
        $self->connect_cli;
        $self->switch_to_shell;
    }else{
        $self->connect_cli;
        $self->switch_to_shell;
        $if_new_handle = $self->if_new_handle;
    }

    if($params{'name_id'} =~ m/[a-zA-Z]/) {
        @result_del = $self->cmd("wgcmd action /certificate/delete purpose=$params{'purpose_name'} name=$params{'name_id'}");
    } else {
        @result_del = $self->cmd("wgcmd action /certificate/delete cert_id=$params{'name_id'}");
    }

    if($result_del[1] =~ m/ok/i) {
        $result_cmd[0] ='ok';
        print("remove the certificate success.\n");
    } else {
        $result_cmd[0] ='nok';
        print("remove the certificate failed.\n");
    }

    $self->disconnect;
    print("restore the dut at the right date.\n");
    @result_date = $self->check_system_time();
    
    return @result_cmd;
}

# /*!
#    @function check the certificate
#    @param control_pc_ip=>'control_pc_ip' - control pc ip
#    @param purpose=>'purpose' - the certificate type, should be proxy or ssl-vpn or web or cli or vpn.
#    @param name_id=>'name_id' -  the name of certificate.
#    @param algorithm=>'algorithm' - the encryption type, should be dsa or rsa.
#    @param keylength=>'keylength' - the encryption length, should be 1024 or 2048.
#    @param type=>'type' - import type, should be ipsec-web-server-other or proxy-authority or proxy-server or proxy-trusted
#    @param SN=>'serial number' - dut SN number
#    @param cacert=>'ca certificate' - dut SN number, could get by API sign_certificate' return
#    @param signcert=>'signcert' - signed certificate, could get by API sign_certificate' return
#    @param date=>'date' - valid date, could get by API sign_certificate' return
#    @return \@result  $result[0]:ok/nok. ok the certificate already add in DUT 
#    @discussion 
#   <h2>Description: </h2> 
#   <pre>cert request
#        check the certificate on DUT.  If result ok, means the ceriticate already import into DUT.</pre>
#   <h2>Example:</h2>
#   <pre>my \@result = $dut->cli->import_cert(name_id=>'selfsignedServer',purpose=>'proxy',algorithm=>'RSA',keylength=>'1024',control_pc_ip=>'192.168.19.253',type=>"proxy-server", SN=>"$seril", cacert=>"$result2[1]", signcert=> "$result2[2]", date=> "$result2[3]");
# */
sub import_cert(){
    my $self = shift;
    my %params = @_;
    my @result_cmd;
    my @result_ca;
    my @result_cert;
    my $if_new_handle = $self->if_new_handle;
    if( ! ( (exists $params{'purpose'} && exists $params{'algorithm'} && exists $params{'keylength'} && exists $params{'control_pc_ip'}  && exists $params{'type'} && exists $params{'SN'}) && (exists $params{'cacert'}) && (exists $params{'signcert'} ) && (exists $params{'date'}) || (exists $params{'subject'}))) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters to import certificate!";
        print("Error parameters to import certificate!");
        if($if_new_handle){
            $self->disconnect;
        }else{
            $self->switch_to_cli;
        }
        return @result_cmd;
    }

    my $ca = join('_',"ca","$params{'SN'}");
    my $cert = join('_',"cert","$params{'SN'}");
    my $caname = join('.',"$ca","pem");
    my $certname = join('.',"$cert","pem");

    if(! exists $params{'subject'}) {
        $params{'subject'} = "c=CN st=SC o=WG cn=WatchGuard";
    }else{
        print "the subject is exists, go on next!.\n";
    }
    my @result_check_1 = $self->check_cert(purpose=>"$params{'purpose'}",algorithm=>"$params{'algorithm'}",keylength=>"$params{'keylength'}", subject=>"$params{'subject'}");
    if($result_check_1[0] =~ m/nok/i and $result_check_1[1] eq 'no cert is add into dut.') {
        print "the env is clear.\n";
    }elsif($result_check_1[0] =~ m/ok/i and $result_check_1[1] eq 'vpn and ca is add into dut.'){
        print "delete vpn cert and ca cert.\n";
        $self->del_cert(purpose_name=>"$params{'purpose'}", name_id=>"$result_check_1[2]");
        $self->del_cert(purpose_name=>"ca", name_id=>"$result_check_1[3]");
    }elsif($result_check_1[0] =~ m/nok/i and $result_check_1[1] eq 'vpn added, no ca.'){
        print "delete the vpn cert only.\n";
        $self->del_cert(purpose_name=>"ca", name_id=>"$result_check_1[2]");
    }elsif($result_check_1[0] =~ m/ok/i and $result_check_1[1] eq 'cert and ca is add into dut.'){
        print "delete cert and ca cert.\n";
        $self->del_cert(purpose_name=>"$params{'purpose'}", name_id=>"$result_check_1[2]");
        $self->del_cert(purpose_name=>"ca", name_id=>"$result_check_1[3]");
    }elsif($result_check_1[0] =~ m/ok/i and $result_check_1[1] eq 'cert add, no ca.'){
        print "delete cert only.\n";
        $self->del_cert(purpose_name=>"$params{'purpose'}", name_id=>"$result_check_1[2]");
    }elsif($result_check_1[0] =~ m/nok/i and $result_check_1[1] eq 'the ca is only added into the dut.'){
        print "delete ca cert only.\n";
        $self->del_cert(purpose_name=>"ca", name_id=>"$result_check_1[2]");
    }else{

    }
    
    my @result_split1 = split('\r',$params{'cacert'});
    open(FH,"> /tmp/$caname") or die "Couldn't open caname for reading!";
    foreach my $lista (@result_split1) {
        print FH "$lista";
    }
    close(FH);

    my @result_split2 = split('\r',$params{'signcert'});
    open(FK,"> /tmp/$certname") or die "Couldn't open certname for reading!";
    foreach my $listb (@result_split2) {
        print FK "$listb";
    }
    close(FK);

    my @result_date;
    if($if_new_handle){
        $self->connect_cli;
        @result_date = $self->cmd("clock date $params{'date'}");
    }else{
        $self->connect_cli;
        $if_new_handle = $self->if_new_handle;
        @result_date = $self->cmd("clock date $params{'date'}");
    }
    print "start to change the Firebox time into the range of certificate valid time\n.";
    if($result_date[1] =~ m/Error/i or $result_date[1] =~ m/failed/i) {
        print("modify the firebox time wrong.\n");
        $result_cmd[0] ='nok';
        $result_cmd[1] ='modify the firebox time wrong.';
        return @result_cmd;
    } else {
        print("import the certificate success.\n"); 
        $result_cmd[0] ='ok';
        $result_cmd[1] ='import the certificate success.';
    }
    
    print "start to import the certificate into the dut.\n.";
    @result_ca = $self->cmd("import certificate $params{'type'} from ftp://automation:password\@$params{'control_pc_ip'}//tmp/$caname");
    if($result_ca[1] =~ m/Error/i or $result_ca[1] =~ m/failed/i) {
        print("import the ca cert failed.\n");
        $result_cmd[0] ='nok';
        $result_cmd[1] ='import the ca cert failed.';
        return @result_cmd;
    } else {
        print("import the certificate success.\n"); 
        $result_cmd[0] ='ok';
        $result_cmd[1] ='import the ca certificate success.';
    }
    
    @result_cert = $self->cmd("import certificate $params{'type'} from ftp://automation:password\@$params{'control_pc_ip'}//tmp/$certname");
    if($result_cert[1] =~ m/Error/i or $result_cert[1] =~ m/failed/i) {
        print("import the certificate failed.\n");
        $result_cmd[0] ='nok';
        $result_cmd[1] ='import the ca cert failed.';
        return @result_cmd;
    } else {
        print("import the certificate success.\n"); 
        $result_cmd[0] ='ok';
        $result_cmd[1] ='import the ca certificate success.';
    }
    
    unlink "/tmp/$caname" or die "Coundn't delete the ca certificate";
    unlink "/tmp/$certname" or die "Coundn't delete the signed certificate";
    
    my @result_check_2 = $self->check_cert(purpose=>"$params{'purpose'}",algorithm=>"$params{'algorithm'}",keylength=>"$params{'keylength'}",subject=>"$params{'subject'}");
    if($result_check_2[0] eq 'ok' and $result_check_2[1] eq 'vpn and ca is add into dut.') {
        $result_cmd[0] ='ok';
        $result_cmd[1] ='import the vpn certificate success.';
        $result_cmd[2] = $result_check_2[2];
        $result_cmd[3] = $result_check_2[3];
    }elsif($result_check_2[0] eq 'ok' and $result_check_2[1] eq 'cert and ca is add into dut.'){
        $result_cmd[0] ='ok';
        $result_cmd[1] ='import the singed certificate success.';
        $result_cmd[2] = $result_check_2[2];
        $result_cmd[3] = $result_check_2[3];
    }elsif($result_check_2[0] eq 'nok' and $result_check_2[1] eq 'no cert is add into dut.'){
        $result_cmd[0] ='nok';
        $result_cmd[1] ='import the singed certificate failed.';
    }else{
        $result_cmd[0] ='nok';
        $result_cmd[1] ='check the certificate failed.';
    }
    
    $self->disconnect();
    return @result_cmd;
}

# /*!
#    @function check the certificate
#    @param name_id=>'name' -  the name of certificate.
#    @param purpose=>'purpose' -  the type of certificate.
#    @param algorithm=>'algorithm' - the encryption type, should be dsa or rsa.
#    @param keylength=>'keylength' - the encryption length, should be 1024 or 2048.
#    @return \@result  $result[0]:ok/nok. $result[1]: the certificate status $result[2]: if VPN certificate, it's VPN ID.
#    @discussion 
#   <h2>Description: </h2> 
#   <pre>cert request
#        check the certificate on DUT.  If result ok, means the ca ceriticate and signed certificate already import into DUT.</pre>
#   <h2>Example:</h2>
#   <pre>my \@result = check_cert(name=>'selfsignedServer', purpose=>'proxy','algorithm=>'RSA',keylength=>'1024');
sub check_cert(){
    my $self = shift;
    my %params = @_;
    my @result_cmd;
    my @pending;
    my @valid;
    my @expired;
    my @result_cert;
    my $certnum = 0;
    my $certname;
    my @result_invalid;
    my $if_new_handle = $self->if_new_handle;
    
    if( ! ( (exists $params{'purpose'} && exists $params{'algorithm'}) && (exists $params{'keylength'}) && (exists $params{'subject'}))) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters to import certificate!";
        print("Error parameters to import certificate!");
        return @result_cmd;
    }

    if($if_new_handle){
            $self->connect_cli;
            @result_cert = $self->cmd("config","show certificate type common");
    }else{
            $self->connect_cli;
            $if_new_handle = $self->if_new_handle;
            @result_cert = $self->cmd("config","show certificate type common");
    }

    #delete the invalid certificate #Add by David 04/28/2013   
    my @result_one = split("Valid Certificate",$result_cert[1]);
    if($result_cert[0] =~ m/Total\s+(\d+)\s+Pending\s+Certificate(s)/i) {
        $certnum = $1; 
        if($certnum > 0) {
            @result_invalid = split("\n",$result_cert[1]);
            foreach my $list (@result_invalid) {
                if($list =~ m/(\w+)\s+\w+\/\s+\w+\s+\w+\s+c=CN\s+st=SC\s+l=CD\s+o=WG\s+cn=WatchGuard/i) {
                    $certname = $1;
                    if($certname =~ m/[a-zA-Z]/) {
                        @result_del = $self->cmd("no certificate name $certname");
                    } else {
                        @result_del = $self->cmd("no certificate $certname");
                    }
                }   
            }
        }
    }
    #get the certificate valid
    my @result_two = split("Expired Certificate",$result_one[1]);
    
    my $vpnid = undef;
    if($result_two[0] =~ m/(\d+)\s+$params{'algorithm'}\s+$params{'keylength'}\s+$params{'subject'}/i) {
        print "the vpn certificate is add into the dut.\n";
        $vpnid = $1;
        if($result_two[0] =~ m/(\w+)\s+ca\s+RSA\s+1024\s+c=US\s+st=Some-State\s+o=wats\s+cn=WATSOpenSSLServer/i) {
            $result_cmd[0] = 'ok';
            $result_cmd[1] = "vpn and ca is add into dut.";
            $result_cmd[2] = "$vpnid";
            $result_cmd[3] = $1;
    }else{
            $result_cmd[0] = 'nok';
            $result_cmd[1] = "vpn added, no ca.";
            $result_cmd[2] = "$vpnid";
        }
    }elsif($result_two[0] =~ m/(\w+)\s+$params{'purpose'}\s+$params{'algorithm'}\s+$params{'keylength'}\s+$params{'subject'}/i){
        print "the certificate is add into the dut.\n";
        $certname = $1;
        if($result_two[0] =~ m/(\w+)\s+ca\s+RSA\s+1024\s+c=US\s+st=Some-State\s+o=wats\s+cn=WATSOpenSSLServer/i) {
            $result_cmd[0] = 'ok';
            $result_cmd[1] = "cert and ca is add into dut.";
            $result_cmd[2] = "$certname";
            $result_cmd[3] = $1;
    }else{
            $result_cmd[0] = 'nok';
            $result_cmd[1] = "cert add, no ca.";
            $result_cmd[2] = "$certname";
        }
    }elsif($result_two[0] =~ m/(\w+)\s+ca\s+RSA\s+1024\s+c=US\s+st=Some-State\s+o=wats\s+cn=WATSOpenSSLServer/i){
        print "the ca is only added into the dut.\n";
        $result_cmd[0] = 'nok';
        $result_cmd[1] = "the ca is only added into the dut.";
        $result_cmd[2] = $1;
    }elsif($result_two[0] =~ m/(\w+)\s+ca\s+RSA\s+1024\s+c=US\s+st=Some-State\s+o=wats\s+cn=WATSOpenSSLServer/i){
        print "the ca is only added into the dut.\n";
        $result_cmd[0] = 'nok';
        $result_cmd[1] = "the ca is only added into the dut.";
        $result_cmd[2] = $1;
    }else{
        print "the certificate is not added into the dut.\n";
        $result_cmd[0] = 'nok';
        $result_cmd[1] = "no cert is add into dut.";
    }

    $self->disconnect();
    return @result_cmd;

}


# /*!
#    @function clear_local_log
#    @param the device version
#    @return 'ok' or 'nok'
#    @discussion
#    <h2>Description: </h2>
#    <pre> clear the log in DUT /var/log</pre>
#    <h2>Example:</h2>
#    <pre>
#        @result = $dut_client->cli->clear_local_log();
#    </pre>
# */   

sub clear_local_log{
    my $self = shift;
    my @result;
    my @logfile;
    my $if_new_handle = $self->if_new_handle;
    $self->connect_shell;
    $self->cmd("cd /var/log");
    @result = $self->cmd("ls -l");
    @logfile = split(/\r\n/,$result[1]);
    foreach (@logfile) {
        $_ =~ s/\s//ig;
        if ($_ =~ m/\dm([a-z]+\.log)/i) {
            $self->cmd("echo > $1");
        }elsif ($_ =~ m/trace/) {
            $self->cmd("rm -r trace");
        }elsif ($_ =~ m/dmesg-boot\.txt/i) {
            $self->cmd("rm dmesg-boot.txt");
        }
    }
    $self->disconnect;
    if($result[0] =~ m/ok/i) {
        return 'ok';
    } else {
        return 'nok';
    }
}

# */!
#    @function set log level
#    @param feature and log level
#    @return 'ok'/"config the $params{'feature'} log level $params{'level'} success." or 'nok'/config the $params{'feature'} log level $params{'level'} failed.
#    @discussion
#    <h2>Description: </h2>
#    <pre> set the log level</pre>
#    <h2>Example:</h2>
#    <pre>
#        @result = $dut_client->cli->set_log_level(feature=>'HTTP-20',level=>'off');
#    </pre>
# */
sub set_log_level{
    my $self = shift;
    my %params = @_;
    my @result_cmd;
    my $type;
    my $feature;
    my $if_new_handle = $self->if_new_handle;

    if( ! ( (exists $params{'feature'} || exists $params{'level'} ) ) ) {
        $result_cmd[0] = "nok";
        $result_cmd[1] = "Error parameters for set log level";
        return @result_cmd;
    }

    if($params{'level'} =~ m/off/i) {
        $type = quotemeta('(None  )');
    }elsif($params{'level'} =~ m/Error/i){
        $type = quotemeta('(Low   )');
    }elsif($params{'level'} =~ m/Warning/i){
        $type = quotemeta('(Medium)');
    }elsif($params{'level'} =~ m/Information/i){
        $type = quotemeta('(High  )');
    }elsif($params{'level'} =~ m/Debug/i){
        $type = quotemeta('(Advanced)');
    }else{
        $result_cmd[0] = 'nok';
        $result_cmd[1] = 'log level error';
    }
    
    $self->connect_cli;
    @result_cmd = $self->cmd("config","log-setting log-level $params{'feature'} $params{'level'}");
    $self->disconnect;
    if($result_cmd[1] =~ m/Error/i or $result_cmd[1] =~ m/failed/i) {
        $result_cmd[0] ='nok';
        $result_cmd[1] ="set $params{'feature'} log level $params{'level'} failed";
        return @result_cmd;
    } else {
        $result_cmd[0] ='ok';
        $result_cmd[1] ="set $params{'feature'} log level $params{'level'} success";
    }
    
    if($params{'feature'} =~ m/TCP\-UDP/i){
        $feature = 'TCP-UDP';
    }else{
        $feature = $params{'feature'};
        $feature =~ s/\-\d+$|-/ /g;
    }

    $self->connect_cli;
    @result_cmd = $self->cmd("show log-setting log-level");
    $self->disconnect;
    if($result_cmd[1] =~ m/$feature\s+:$params{'level'}\s+$type/i) {
        $result_cmd[0] ='ok';
        $result_cmd[1] ="config the $params{'feature'} log level success.";
    }else{
        $result_cmd[0] ='nok';
        $result_cmd[1] ="config the $params{'feature'} log level failed.";
    }
    
    return @result_cmd;
}

# /*!
#    @function restore_clocktime
#    @param the backup time.
#    @return the month number
#    @discussion 
#    <h2>Description: </h2>
#    <pre>this method return the month number
#    <h2>example:</h2>
#    <pre>  my @backup = ($time_month,$time_day,$time_year);
#    <pre>  my $newmonth = $dut->restore_clocktime(@backup);</pre>
# */
sub restore_clocktime{
    my $self = shift;
    my @backup = @_;
    $self->connect_cli();
    $self->cmd("clock date $backup[0]/$backup[1]/$backup[2]");
    $self-> disconnect();
}

sub check_system_time{
    my $self = shift;
    my $month_dut = undef;
    my $day_dut = undef;
    my $year_dut = undef;
    my $month = undef;
    my $day = undef;
    my $year = undef;
    my $if_new_handle = $self->if_new_handle;
    #############2012-02-13 add code to avoid sn issue in Seattle###########################
    my $retry_time = 1;
    print "sleep 3 seconds...";
    sleep 3;
    my $result = $self->connect_cli();
    while ($result ne 'ok') {
        $retry_time++;
        print "sleep 3 seconds...";
        sleep 3;
        $result = $self->connect_cli();
        if ($retry_time == 10) {
            last;
        }
    }
    ########################################################################################
    my @date_dut = $self->cmd('show clock');
    my $date = `date 2>&1`;
    $date_dut[1] =~ s/\n|\t/ /g;
    if ($date_dut[1] =~ m/.*System Clock.*(\w+)\s+(\w+)\s+(\d+)\s+(.*)\s+\w+\s+(\w+)/i) {
        $month_dut = $2;
        $day_dut = $3;
        $year_dut = $5;
        print "Match the dut date success.\n";
    }else{
        print "Can not catch the dut date.\n";
        $self->disconnect();
        return "nok";
    }
    if ($date =~ m/(\w+)\s+(\w+)\s+(\d+)\s+(\d+)\:(\d+)\:(\d+)\s+\w+\s+(\d+)/i) {
        $month = $2;
        $day = $3;
        $year = $7;
        print "Match the pc date success.\n";
    }else{
        print "Can not match the pc date.\n";
        $self->disconnect();
        return "nok";
    }
    if ($month_dut eq $month and $year_dut eq $year) {
        print "The box time is right.\n";
    }else{
        print "The box time need modify.\n";
        my $new_month = $self->change_timetype($month);
        my @result = $self->cmd("clock date $new_month/$day/$year");
        if ($result[0] eq 'nok') {
            print "Modify the box clock failed.\n";
            $self->disconnect();
            return "nok";
        }else{
            print "Modify the box clock success.\n";
            $self->disconnect();
            return "ok";
        }
    }
    $self->disconnect();
    return "ok";
}

# /*!
#    @function buildin_ike_policy 
#    @param $flag (the string: enable/disable)
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    enable or disable the hidden policy Allow-IKE-to-Firebox</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $mycli->buildin_ike_policy(enable);</pre>
# */

sub buildin_ike_policy{
    my $self=shift;
        my $flag=shift;
    my $if_new_handle = $self->if_new_handle;
        my $result = $self->connect_cli;
        my @cli_cmd;
        my @result_cmd;
    if ($result eq 'nok'){
               print "XtmCli:buildin_ike_policy: get connect_cli nok, return nok...\n";
               $result_cmd[0] = 'nok';
               return @result_cmd;
        }
        if ($flag ne 'enable' and $flag ne 'disable'){
            print "XtmCli:buildin_ike_policy: param must be enable or disable...\n";
            $result_cmd[0] = 'nok';
               return @result_cmd;
        }
        if ($flag eq 'enable'){
            @cli_cmd=(
            "config",
            "vpn-setting built-in-ipsec-policy enable",
                       "show vpn-setting"
        );
        } elsif ($flag eq 'disable'){
            @cli_cmd=(
            "config",
            "no vpn-setting built-in-ipsec-policy enable",
                       "show vpn-setting"
        );
        }
        @result_cmd = $self->cmd(@cli_cmd);
        if($if_new_handle){
        $self->disconnect;
    } else{
        $self->switch_to_cli;
    }
        return @result_cmd;
}

# /*!
#    @function check_autodiag_tunnel_config
#    @param $result[1] -  from @result=$dut_cli_cmd 
#    @param tunnel_cnt=>'cnt' -  the count of tunnels [required].
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Judge tunnels info</pre>
#    <h2>Example:</h2>
#    <pre> 
#           my %tunnel_1=(
#               tunnel1 => {
#                    name => "tunnel.1",
#                    pfs => "Disabled",
#                    dhgroup => "2",
#                    proposal_number => "1",
#                    proposal_1_name => "ESP-AES-SHA1",
#                    proposal_1_esp_encry => "AES",
#                    proposal_1_esp_keylen => "32",
#                    proposal_1_esp_auth => "SHA",
#                    proposal_1_esp_lifetime => "28800",
#                    proposal_1_esp_lifebyte => "128000",
#                    tunelroute_number => "1",
#                    tunnelroute_1_direct => "BOTH",
#                    tunnelroute_1_stream => "3.3.3.3\/32<\->4.4.4.4\/32",
#                }
#           );
#
#           my %tunnel_2=(
#               tunnel2 => {
#                    name => "tunnel.2",
#                    pfs => "Enabled",
#                    dhgroup => "2",
#                    proposal_number => "2",                                            
#                    proposal_1_name => "phase2_proposal.2",
#                    proposal_1_esp_encry => "DES",                                            
#                    proposal_1_esp_auth => "SHA",                                          
#                    proposal_1_esp_lifetime => "3600", 
#                    proposal_1_esp_lifebyte => "30000",        
#                    proposal_2_name => "ESP-AES-MD5",
#                    proposal_2_esp_encry => "AES",                                            
#                    proposal_2_esp_keylen => "32",
#                    proposal_2_esp_auth => "MD5",
#                    proposal_2_esp_lifetime => "28800",
#                    proposal_2_esp_lifebyte => "128000",                                           
#                    tunelroute_number => "2",
#                    tunnelroute_1_direct => "BOTH",
#                    tunnelroute_1_nattype => "1\-1",                                        
#                    tunnelroute_1_stream => "192.168.4.0/255.255.255.0<->192.168.2.0/255.255.255.0",
#                    tunnelroute_1_natto => "55.55.55.0/255.255.255.0",
#                    tunnelroute_2_direct => "BOTH",
#                    tunnelroute_2_stream => "1.1.1.1/32<->2.2.2.2/32",                                     
#                }
#           );
#
#           my $return = check_autodiag_tunnel_config (tunnel_info => "$result[1]", tunnel_cnt=>"2", %tunnel_1, %tunnel_2);
#
#    </pre>
# */
sub check_autodiag_tunnel_config {
    my $this = shift;
    my %args = @_;
    my $autodiag = $args{tunnel_info};
    if (!defined($args{tunnel_cnt})){
        print "tunnel count must be provided\n";
        return 'nok';
    }       
    my $tunnel_cnt = $args{tunnel_cnt};
   delete $args{tunnel_info};
   # delete $args{tunnel_cnt};
    my @tmp = split(/\[.*?\]/,$autodiag);

    if ($tmp[2] !~ /\s+\"$tunnel_cnt\" tunnel\(s\) are found using the previous gateway/){ 
        print "Tunnel count is not match.\n$tmp[2]\n";
        return 'nok';
    }

    #Delete key(tunnel_cnt) from args
    delete $args{tunnel_cnt};

    #Split different tunnels
    my @diff_tunnel = split(/\s+Name/, $tmp[2]);
    foreach my $key (keys (%args)){
        my $ep = $args{$key};   
        

        #find which tunnel of cmdEcho is right to $ep
        my $err_flag;
        my $tunnel_index;
        for ($tunnel_index = 1;$tunnel_index <@diff_tunnel;$tunnel_index++) {
            if($diff_tunnel[$tunnel_index] =~ /\"$ep->{name}\"/i) {
                $err_flag=0;
                last;
            } else {
                $err_flag=1;
            }   
        }       

        if ($err_flag eq "1") {     
            print "The tunnel info is not correct\n";
            return 'nok';
        }

        #******************Deal with PFS and DH-Group start********************
        if (exists $$ep{pfs}){
            if($diff_tunnel[$tunnel_index] !~ /PFS[:"\s]+$ep->{pfs}\"/i) {
                print "The $ep->{name}: PFS=$ep->{pfs} is not correct\n";
                return 'nok';
            }
        }   
        if (exists $$ep{dhgroup}){
            if($diff_tunnel[$tunnel_index] !~ /DH\-Group[:"\s]+$ep->{dhgroup}\"/i) {
                print "The $ep->{name}: DH-Group=$ep->{dhgroup} is not correct\n";
                return 'nok';
            }
        }   
        #******************Deal with PFS and DH-Group end********************** 
        
        #******************Deal with proposal info start******************
        if (exists $$ep{proposal_number}) {
            
            #Now key(proposal_number) exists, start dealing with information about it
            if($diff_tunnel[$tunnel_index] !~ /Number\s+of\s+Proposals\:\s+\"$ep->{proposal_number}\"/i) {
                print "The proposal num is wrong\n";
                return 'nok';
            }   
            
            #Split different proposal                   
            my @diff_proposal = split(/\s+Proposal\s+/, $diff_tunnel[$tunnel_index]);
        
            for (my $proposal_num=1;$proposal_num<=$ep->{proposal_number};$proposal_num++) {
                my $regexp=0;                              #initialize regexp result        
                my $proposal_name_err_times=0;             #initialize proposal_name regexp error times 
                my $proposal_esp_encry_err_times=0;        #initialize proposal_esp_encry regexp error times            
                my $proposal_esp_auth_err_times=0;         #initialize proposal_esp_auth regexp error times
                my $proposal_esp_lifetime_err_times=0;     #initialize proposal_esp_lifetime regexp error times
                my $proposal_esp_lifebyte_err_times=0;     #initialize proposal_esp_lifebyte regexp error times
                my $proposal_esp_keylen_err_times=0;       #initialize proposal_esp_keylen regexp error times
                    
                for (my $proposal_index=1;$proposal_index<=$ep->{proposal_number};$proposal_index++) {                                                                                  
                    
                    #Deal with proposal_name
                    my $proposal_name="proposal_" . $proposal_num . "_name";    
                    if (exists $$ep{$proposal_name}){   
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_name}\"/i) {
                            $proposal_name_err_times++;
                            if ($proposal_name_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num name info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }   
                    
                    #Deal with proposal_esp_encry
                    my $proposal_esp_encry="proposal_" . $proposal_num . "_esp_encry";              
                    if (exists $$ep{$proposal_esp_encry}){  
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_esp_encry}\"/i) {
                            $proposal_esp_encry_err_times++;
                            if ($proposal_esp_encry_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num esp_encry info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }   
                    
                    #Deal with proposal_esp_auth
                    my $proposal_esp_auth="proposal_" . $proposal_num . "_esp_auth";
                    if (exists $$ep{$proposal_esp_auth}){   
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_esp_auth}\"/i) {
                            $proposal_esp_auth_err_times++;
                            if ($proposal_esp_auth_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num esp_auth info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }   
                    
                    #Deal with proposal_esp_lifetime
                    my $proposal_esp_lifetime="proposal_" . $proposal_num . "_esp_lifetime";
                    if (exists $$ep{$proposal_esp_lifetime}){   
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_esp_lifetime}\(seconds\)\"/i) {
                            $proposal_esp_lifetime_err_times++;
                            if ($proposal_esp_lifetime_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num esp_lifetime info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Deal with proposal_esp_lifebyte
                    my $proposal_esp_lifebyte="proposal_" . $proposal_num . "_esp_lifebyte";                
                    if (exists $$ep{$proposal_esp_lifebyte}){   
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_esp_lifebyte}\(kbytes\)\"/i) {
                            $proposal_esp_lifebyte_err_times++;
                            if ($proposal_esp_lifebyte_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num esp_lifebyte info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Deal with proposal_esp_keylen
                    my $proposal_esp_keylen="proposal_" . $proposal_num . "_esp_keylen";
                    if (exists $$ep{$proposal_esp_keylen}){ 
                        if ($diff_proposal[$proposal_index] !~ /\"$ep->{$proposal_esp_keylen}\(bytes\)\"/i) {
                            $proposal_esp_keylen_err_times++;
                            if ($proposal_esp_keylen_err_times == $ep->{proposal_number}) {
                                print "The $ep->{name} proposal_$proposal_num esp_keylen info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Right regexp all
                    $regexp=1;
                    last;
                }

                if ($regexp != 1) {
                    print "The $ep->{name} proposal_$proposal_num information is not correct\n";
                    return 'nok';
                }   
            }
        }   
        #******************Deal with proposal info end******************
        
        #******************Deal with Tunnel Routes info start******************
        if (exists $$ep{tunelroute_number}) {
            
            #Now key(tunelroute_number) exists, start dealing with information about it
            if($diff_tunnel[$tunnel_index] !~ /Number\s+of\s+Tunnel\s+Routes[\s:"]+$ep->{tunelroute_number}\"/i) {
                print "The Tunnel Routes num is wrong\n";
                return 'nok';
            }   
            
            #Split different Tunnel Routes                  
            my @diff_tunnel_routes = split(/Direction/, $diff_tunnel[$tunnel_index]);
        
            for (my $tunnel_routes_num=1;$tunnel_routes_num<=$ep->{tunelroute_number};$tunnel_routes_num++) {
                my $regexp=0;                              #initialize regexp result        
                my $tunnel_routes_direct_err_times=0;      #initialize tunnel_routes_direct regexp error times 
                my $tunnel_routes_stream_start_err_times=0;      #initialize tunnel_routes_stream regexp error times
                my $tunnel_routes_stream_end_err_times=0;      #initialize tunnel_routes_stream regexp error times
                my $tunnel_routes_nattype_err_times=0;     #initialize tunnel_routes_nattype regexp error times
                my $tunnel_routes_natto_err_times=0;       #initialize tunnel_routes_natto regexp error times           
                    
                for (my $tunnel_routes_index=1;$tunnel_routes_index<=$ep->{tunelroute_number};$tunnel_routes_index++) {                                                                                     
                    
                    #Deal with tunnel_routes_direct                 
                    my $tunnel_routes_direct="tunnelroute_" . $tunnel_routes_num . "_direct";
                    if (exists $$ep{$tunnel_routes_direct}){    
                        if ($diff_tunnel_routes[$tunnel_routes_index] !~ /\"$ep->{$tunnel_routes_direct}\"/i) {
                            $tunnel_routes_direct_err_times++;
                            if ($tunnel_routes_direct_err_times == $ep->{tunelroute_number}) {
                                print "The $ep->{name} tunnel_routes_$tunnel_routes_num direct info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Deal with tunnel_routes_stream_start               
                    my $tunnel_routes_stream_start="tunnelroute_" . $tunnel_routes_num . "_stream_start";
                    if (exists $$ep{$tunnel_routes_stream_start}){  
                        #if exist netmask , delete netmask
                        my $start_ip;
                        if ($ep->{$tunnel_routes_stream_start} =~ /\//i) {
                            my @start_ip_list = split(/\//,$ep->{$tunnel_routes_stream_start});
                            $start_ip = $start_ip_list[0];
                        } else {
                            $start_ip = $ep->{$tunnel_routes_stream_start};
                        }   
                        
                        if ($diff_tunnel_routes[$tunnel_routes_index] !~ /$start_ip.*\<\-\>/i) {
                            $tunnel_routes_stream_start_err_times++;
                            if ($tunnel_routes_stream_start_err_times == $ep->{tunelroute_number}) {
                                print "The $ep->{name} tunnel_routes_$tunnel_routes_num stream start info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Deal with tunnel_routes_stream_end             
                    my $tunnel_routes_stream_end="tunnelroute_" . $tunnel_routes_num . "_stream_end";
                    if (exists $$ep{$tunnel_routes_stream_end}){    
                        #if exist netmask , delete netmask
                        my $end_ip;
                        if ($ep->{$tunnel_routes_stream_end} =~ /\//i) {
                            my @end_ip_list = split(/\//,$ep->{$tunnel_routes_stream_end});
                            $end_ip = $end_ip_list[0];
                        } else {
                            $end_ip = $ep->{$tunnel_routes_stream_end};
                        }   
                        
                        if ($diff_tunnel_routes[$tunnel_routes_index] !~ /\<\-\>.*$end_ip/i) {
                            $tunnel_routes_stream_end_err_times++;
                            if ($tunnel_routes_stream_end_err_times == $ep->{tunelroute_number}) {
                                print "The $ep->{name} tunnel_routes_$tunnel_routes_num stream end info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Deal with tunnel_routes_nattype                    
                    my $tunnel_routes_nattype="tunnelroute_" . $tunnel_routes_num . "_nattype";
                    if (exists $$ep{$tunnel_routes_nattype}){   
                        if ($diff_tunnel_routes[$tunnel_routes_index] !~ /\"$ep->{$tunnel_routes_nattype}\"/i) {
                            $tunnel_routes_nattype_err_times++;
                            if ($tunnel_routes_nattype_err_times == $ep->{tunelroute_number}) {
                                print "The $ep->{name} tunnel_routes_$tunnel_routes_num nattype info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                                                        
                    #Deal with tunnel_routes_natto                  
                    my $tunnel_routes_natto="tunnelroute_" . $tunnel_routes_num . "_natto";
                    if (exists $$ep{$tunnel_routes_natto}){ 
                        #if exist netmask , delete netmask
                        my $natto_ip;
                        if ($ep->{$tunnel_routes_natto} =~ /\//i) {
                            my @natto_ip_list = split(/\//,$ep->{$tunnel_routes_natto});
                            $natto_ip = $natto_ip_list[0];
                        } else {
                            $natto_ip = $ep->{$tunnel_routes_natto};
                        }
                        
                        if ($diff_tunnel_routes[$tunnel_routes_index] !~ /$natto_ip/i) {
                            $tunnel_routes_natto_err_times++;
                            if ($tunnel_routes_natto_err_times == $ep->{tunelroute_number}) {
                                print "The $ep->{name} tunnel_routes_$tunnel_routes_num natto info is not correct\n";
                                return 'nok';
                            }                           
                            next;
                        }   
                    }
                    
                    #Right regexp all
                    $regexp=1;
                    last;
                }

                if ($regexp != 1) {
                    print "The $ep->{name} tunnel_routes_$tunnel_routes_num information is not correct\n";
                    return 'nok';
                }   
            }
        }   
        #******************Deal with Tunnel Routes info end******************
    }

    return 'ok';
}


# /*!
#    @function check_autodiag_ike_sa
#    @param $result[1] -  from @result=$dut_cli_cmd 
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Judge ike_sa info</pre>
#    <h2>Example:</h2>
#    <pre> 
#           my %gateway=(
#               one => {
#                   name => "gateway.2",
#                   state => "SA Mature",
#                   my_address => "192.168.13.123:500",
#                   peer_address => "192.168.11.20:500",                                           
#               }
#           );
#           
#           my $return = check_autodiag_ike_sa (autodiag_info => "$result[1]", %gateway);
#
#    </pre>
# */
sub check_autodiag_ike_sa  {
    my $this = shift;   
    my %args = @_;
    my $autodiag = $args{ike_info};
    delete $args{ike_info};
    my @tmp = split(/\[.*?\]/,$autodiag);
    #Split different gateway
    my @diff_gateway = split(/\s+Name\:/, $tmp[3]);
    foreach(keys (%args)){
        my $ep = $args{$_}; 
        
        #find which gateway of cmdEcho is right to $ep
        my $err_flag;
        my $gateway_index;
        for ($gateway_index = 1;$gateway_index < 2;$gateway_index++) {      
            if($diff_gateway[$gateway_index] =~ m/$ep->{name}/i and $diff_gateway[$gateway_index] =~ m/State:\s+\"$ep->{state}/i and $diff_gateway[$gateway_index] =~ m/My\s+Address:\s+$ep->{my_address}/i and $diff_gateway[$gateway_index] =~ m/Peer\s+Address:\s+$ep->{peer_address}/i) {
                $err_flag=0;
                last;
            } else {
                $err_flag=1;
            }   
        }       

        if ($err_flag eq "1") {     
            print "The gateway info:$ep->{name} is not correct\n";
            return 'nok';
        }
    }
    return 'ok';
}


# /*!
#    @function check_autodiag_ipsec_sa
#    @param $result[1] -  from @result=$dut_cli_cmd 
#    @param ipsec_sa_group_cnt=>'cnt' -  the count of ipsec_sa_group [required].
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Judge ipsec_sa info</pre>
#    <h2>Example:</h2>
#    <pre> 
#           my %ipsec_sa_group_1=(
#               group_1 => {                                           
#                    outbound_tunnel_endpoint => "192.168.4.2->192.168.3.50",
#                    outbound_tunnel_selector_start => "1.1.1.0/24",  
#                    outbound_tunnel_selector_end => "22.22.22.0/24",                                   
#                    inbound_tunnel_endpoint => "192.168.3.50->192.168.4.2",
#                    inbound_tunnel_selector_start => "22.22.22.0/24", 
#                    inbound_tunnel_selector_end => "1.1.1.0/24", 
#               }
#           );
#
#           my %ipsec_sa_group_2=(
#               group_2 => {
#                    outbound_tunnel_endpoint => "192.168.4.2->192.168.3.50",
#                    outbound_tunnel_selector_start => "21.21.21.0/24 -> 172.16.1.0/24",
#                    outbound_tunnel_selector_end => "172.16.1.0/24",                       
#                    inbound_tunnel_endpoint => "192.168.3.50->192.168.4.2",
#                    inbound_tunnel_selector_start => "172.16.1.0/24 -> 21.21.21.0/24",
#                    inbound_tunnel_selector_end => "21.21.21.0/24",
#               }
#           );
#           
#           my $return = check_autodiag_ipsec_sa (autodiag_info => "$result[1]", ipsec_sa_group_cnt => "2", %ipsec_sa_group_1, %ipsec_sa_group_2);
#
#    </pre>
# */
sub check_autodiag_ipsec_sa  {
    my $this = shift;   
    my %args = @_;
    my $autodiag = $args{ipsec_sa_info};
    delete $args{ipsec_sa_info};
    
    if (!defined($args{ipsec_sa_group_cnt})){
        print "ipsec_sa_group count must be provided\n";
        return 'nok';
    }
  
    my $ipsec_sa_cnt = $args{ipsec_sa_group_cnt} * 2;
 
    my @tmp = split(/\[.*?\]/,$autodiag);
    if ($tmp[4] !~ /\"$ipsec_sa_cnt\"\s+IPSEC\s+SA\(s\)\s+are\s+found/i){ 
        print "ipsec_sa_group count is not match.\n$tmp[4]\n";
        return 'nok';
    }
 #Delete key(ipsec_sa_group_cnt) from args
 delete $args{ipsec_sa_group_cnt};  
 #Get bounds information
 my @bounds_info_all = split(/IPSEC\s+SA\(s\)\s+are\s+found/, $tmp[4]);
 my $bounds_info=$bounds_info_all[1];
 
 my $base_prompt;
 my $another_prompt;
 
 if($bounds_info =~ /^[\r\n\s]+\"outbound\"/i) {
    $base_prompt = "OUTBOUND";
    $another_prompt = "INBOUND";
 } else {
    $base_prompt = "INBOUND";
    $another_prompt = "OUTBOUND";
 }
 
 #Split different bounds
 my @diff_bounds = split(/\s+\"$base_prompt\"/, $bounds_info);
    
    foreach(keys (%args)){
        my $ep = $args{$_}; 
        my $regexp;
        
        for (my $bound_index = 1;$bound_index <@diff_bounds;$bound_index++) {               
            $regexp=1;    #initialize right regexp result
            
            #Split outbound inbound
            my @out_in_bound_info = split(/\s+\"$another_prompt\"/, $diff_bounds[$bound_index]);
            
            if ($base_prompt =~ /out/i) {
                $out_bound_info=$out_in_bound_info[0];
                $in_bound_info=$out_in_bound_info[1];
            } else {
                $out_bound_info=$out_in_bound_info[1];
                $in_bound_info=$out_in_bound_info[0];
            }   
            #my $out_bound_info=$out_in_bound_info[0];
            #my $in_bound_info=$out_in_bound_info[1];
            
            #***********Deal with outbound start**********
            if (exists $$ep{outbound_tunnel_endpoint}) {                                
                my $pattern = qr/Tunnel\s+Endpoint[\s:"]+$ep->{outbound_tunnel_endpoint}\"/i;
                if ($out_bound_info !~ $pattern) {
                    $regexp=0;
                    next;
                }       
            }   
            
            if (exists $$ep{outbound_tunnel_selector_start}) {
                #if exist netmask , delete netmask
                my $tunnel_selector_start;
                if ($ep->{outbound_tunnel_selector_start} =~ /\//i) {
                    my @tunnel_selector_start_list = split(/\//,$ep->{outbound_tunnel_selector_start});
                    $tunnel_selector_start = $tunnel_selector_start_list[0];
                } else {
                    $tunnel_selector_start = $ep->{outbound_tunnel_selector_start};
                }   
                
                my $pattern = qr/Tunnel\s+Selector[:"\s]+$tunnel_selector_start/i;
                if ($out_bound_info !~ $pattern) {
                    print "************************";
                    $regexp=0;
                    next;
                }       
            }   
            
            if (exists $$ep{outbound_tunnel_selector_end}) {
                #if exist netmask , delete netmask
                my $tunnel_selector_end;
                if ($ep->{outbound_tunnel_selector_end} =~ /\//i) {
                    my @tunnel_selector_end_list = split(/\//,$ep->{outbound_tunnel_selector_end});
                    $tunnel_selector_end = $tunnel_selector_end_list[0];
                } else {
                    $tunnel_selector_end = $ep->{outbound_tunnel_selector_end};
                }   
                
                my $pattern = qr/$tunnel_selector_end/i;
                if ($out_bound_info !~ $pattern) {
                    $regexp=0;
                    next;
                }           
            }   
            #***********Deal with outbound end**********
            
            #***********Deal with inbound start**********
            if (exists $$ep{inbound_tunnel_endpoint}) {                             
                my $pattern = qr/Tunnel\s+Endpoint[\s:"]+$ep->{inbound_tunnel_endpoint}\"/i;
                if ($in_bound_info !~ $pattern) {
                    $regexp=0;
                    next;
                } 
            } 

            if (exists $$ep{inbound_tunnel_selector_start}) {
                #if exist netmask , delete netmask
                my $tunnel_selector_start;
                if ($ep->{inbound_tunnel_selector_start} =~ /\//i) {
                    my @tunnel_selector_start_list = split(/\//,$ep->{inbound_tunnel_selector_start});
                    $tunnel_selector_start = $tunnel_selector_start_list[0];
                } else {
                    $tunnel_selector_start = $ep->{inbound_tunnel_selector_start};
                }   
                
                my $pattern = qr/Tunnel\s+Selector[:"\s]+$tunnel_selector_start/i;
                if ($in_bound_info !~ $pattern) {
                    $regexp=0;
                    next;
                }       
            }   
            
            if (exists $$ep{inbound_tunnel_selector_end}) {
                #if exist netmask , delete netmask
                my $tunnel_selector_end;
                if ($ep->{inbound_tunnel_selector_end} =~ /\//i) {
                    my @tunnel_selector_end_list = split(/\//,$ep->{inbound_tunnel_selector_end});
                    $tunnel_selector_end = $tunnel_selector_end_list[0];
                } else {
                    $tunnel_selector_end = $ep->{inbound_tunnel_selector_end};
                }   
                
                my $pattern = qr/$tunnel_selector_end/i;
                if ($in_bound_info !~ $pattern) {
                    $regexp=0;
                    next;
                }           
            }   
            #***********Deal with inbound end**********
            last;
        }
        
        if ($regexp != 1) {
            print "The information is not correct:\n$ep->{outbound_tunnel_endpoint}\n$ep->{inbound_tunnel_endpoint}\n";
            return 'nok';
        }   
        
    }   
        
 return 'ok';
}   


# /*!
#    @function check_autodiag_ipsec_sp
#    @param $result[1] -  from @result=$dut_cli_cmd 
#    @param ipsec_sp_cnt=>'cnt' -  the count of ipsec_sp_group [required].
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre>  Judge ipsec_sp info</pre>
#    <h2>Example:</h2>
#    <pre> 
#           my %ipsec_sp=(
#               ipsec_sp_info => {                                         
#                   tunnel_endpoint => "192.168.13.123->192.168.11.20",
#                   tunnel_selector_start => "192.168.4.0/24",    
#                   tunnel_selector_end => "192.168.2.0/24",                                     
#               }
#           );
#           
#           my $return = check_autodiag_ipsec_sp (autodiag_info => "$result[1]", ipsec_sp_cnt => "1", %ipsec_sp);
#
#    </pre>
# */
sub check_autodiag_ipsec_sp   {
    my $this = shift;   
    my %args = @_;
    my $autodiag = $args{ipsec_sp_info};
    delete $args{ipsec_sp_info};

    if (!defined($args{ipsec_sp_cnt})){
        print "ipsec_sp count must be provided\n";
        return 'nok';
    }
        
    my $ipsec_sp_cnt = $args{ipsec_sp_cnt};
    
    my @tmp = split(/\[.*?\]/,$autodiag);

    if ($tmp[5] !~ /\"$ipsec_sp_cnt\"\s+IPSEC\s+SP\(s\)\s+are\s+found/i){ 
        print "ipsec_sp count is not match.\n$tmp[5]\n";
        return 'nok';
    }

    #Delete key(ipsec_sp_cnt) from args
    delete $args{ipsec_sp_cnt}; 

    #Split different bounds
    my @diff_ipsec_sp = split(/\s+\#\d+/, $tmp[5]);
                
    foreach(keys (%args)){
         
            #if exist netmask , delete netmask
            my $ep = $args{$_}; 
            my $tunnel_selector_start;
            if ($ep->{tunnel_selector_start} =~ /\//i) {
                my @tunnel_selector_start_list = split(/\//,$ep->{tunnel_selector_start});
                $tunnel_selector_start = $tunnel_selector_start_list[0];
            } else {
                $tunnel_selector_start = $ep->{tunnel_selector_start};
            }

            my $tunnel_selector_end;
            if ($ep->{tunnel_selector_end} =~ /\//i) {
                my @tunnel_selector_end_list = split(/\//,$ep->{tunnel_selector_end});
                $tunnel_selector_end = $tunnel_selector_end_list[0];
            } else {
                $tunnel_selector_end = $ep->{tunnel_selector_end};
            }
            
            for (my $ipsec_sp_index = 1;$ipsec_sp_index <@diff_ipsec_sp;$ipsec_sp_index++) {
                my $pattern = qr/Tunnel\s+Endpoint[\s:"]+$ep->{tunnel_endpoint}\"\s+Tunnel\s+Selector[\s:"]+$tunnel_selector_start.*?$tunnel_selector_end/is;
                if ($diff_ipsec_sp[$ipsec_sp_index] !~ $pattern) {
                    next;
                } else {
                    $regexp=1;
                    last;
                }
            }   
        if ($regexp != 1) {
            print "The ipsec_sp info:\n$ep->{tunnel_endpoint}\n$ep->{tunnel_selector}\nis not correct\n";
            return 'nok';
        }       
    }
    return 'ok';
}   




#Gateway "gateway.2" contains "1" gateway endpoint(s). 
#          Gateway Endpoint #1 (name "gateway.2")
#                Mode: Main      PFS: Disabled
#                DPD: Enabled    Keepalive: Disabled
#                Local ID<->Remote ID: {IP_ADDR(192.168.13.123) <-> IP_ADDR(192.168.11.20)}
#                Local GW_IP<->Remote GW_IP: {192.168.13.123 <-> 192.168.11.20}
#                Outgoing Interface: eth0 (ifIndex=2, ifMark=0x10000, linkStatus=2)
#          Gateway Endpoint #2 (name "gateway.2")
#                Mode: Main      PFS: Disabled
#                DPD: Enabled    Keepalive: Disabled
#                Local ID<->Remote ID: {IP_ADDR(192.168.13.123) <-> IP_ADDR(192.168.11.20)}
#                Local GW_IP<->Remote GW_IP: {192.168.13.123 <-> 192.168.11.20}
#                Outgoing Interface: eth0 (ifIndex=2, ifMark=0x10000, linkStatus=2)
#

sub check_autodiag_gateway_config{
    my $this = shift;
    my %args = @_;
    my $autodiag = $args{gateway_info};
    if (!defined($args{gateway_name})){
        print "Gateway name must be provided\n";
        return 'nok';
    }
    if (!defined($args{gateway_cnt})){
        print "Gateway count must be provided\n";
        return 'nok';
    }
    if (defined($args{check_cnt})){
        my $check_cnt = $args{check_cnt};
        delete $args{check_cnt};
    }
    my $gateway_name = $args{gateway_name};
    my $gateway_cnt = $args{gateway_cnt};
    #print "GW name $gateway_name\n";
    #print "GW cnt $gateway_cnt\n";
    delete $args{gateway_info};
    delete $args{gateway_name};
    delete $args{gateway_cnt};
    
    #print %args;
    my @tmp = split(/\[.*?\]/,$autodiag);
    #print $tmp[1];
    #return;
    my $err_flag = 0;
    if ($tmp[1] !~ /\s+Gateway \"$gateway_name\" contains \"$gateway_cnt\" gateway endpoint\(s\)\./){
        print "\nGateway \"$gateway_name\" contains \"$gateway_cnt\" gateway endpoint\(s\)\.\n";
        print "Header not match.\n$tmp[1]\n";
        return 'nok';
    }
    
    #my @endpoints = split(/\s+Gateway Endpoint\s/, $tmp[1]);
    my $i = 0;
    foreach(keys (%args)){
        my $ep = $args{$_};
        my $pattern = /Gateway Endpoint \#$ep->{id} \(name \"$gateway_name\*{0,1}\d{0,3}\"\)
\s+Mode\:\s+$ep->{mode}\s+PFS\:\s+$ep->{pfs}
\s+DPD\:\s+$ep->{dpd}\s+Keepalive\:\s+$ep->{keepalive}
\s+Local ID\<\-\>Remote ID\:\s+\{$ep->{local_id}\s+\<\-\>\s+$ep->{remote_id}\}
\s+Local GW\_IP\<\-\>Remote GW\_IP\: \{$ep->{local_ip}\s+\<\-\>\s+$ep->{remote_ip}\}
\s+Outgoing Interface\:\s+$ep->{if}/i;
        if($tmp[1] !~ $pattern){
            print "Not match\n";
            print "$pattern";
            print "actual:\n$tmp[1]\n";
            return 'nok';
        }
        $i++;
    }
    if (($i ne $gateway_cnt) && (defined($check_cnt)) && ($check_cnt eq "yes")){
        print "gateway cnt is not the same: $gateway_cnt, actual $i\n";
        return 'nok'
    }
    return 'ok';
}


sub get_autodiag_log{
    my $autodiag = shift;
    my @tmp = split(/\n\[[\w\d\-\_\(\)\s]*\]\n/,$autodiag);
    return $tmp[6];
}


# /*!
#    @function import_dr_config 
#    @param $type $dr_config
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    import to route config file to firebox</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $type = "ospf";
#    <pre>    my $dr_config = "xxxxx";
#    <pre>    my $mycli->import_dr_config($type,$dr_config);</pre>
# */

sub import_dr_config {
        my $self=shift;
        my $type=shift;
        my $dr_config=shift;;

        my $cliresult = $self->connect_cli; 
        my $if_new_handle = $self->if_new_handle;
        print "XtmCli: if_new_handle=$if_new_handle\n";
        
        if($cliresult eq 'nok'){
            print "XtmCli:connect cli fail, return nok\n";
            return 'nok';
        }

        my $configfile_name = "/tmp/0606_drconfig.txt";
        my @esult;

        #Obtain the Server IP info
        my $fileserver ;
        my @interface_result = `ifconfig | grep "inet addr" 2>&1`;

        my $manage_ip_address = $self->host_ip;
        foreach my $line (@interface_result) {
           chomp($line);
           if ($line =~ m/.*inet addr:(.*)Bcast.*/) {
                                $_ = $1;
                                s/\s+$//;
                                my $string = $_;
                                my ($eth1_ip) = $string =~ /(\d*\.\d*\.\d*\.)/g;
                                print $eth1_ip."\n";
                                if ($manage_ip_address =~ /$eth1_ip/g) {
                                        $fileserver = $string;
                                        last;
                                }

                }
        }
       # print "the config file at server info: $fileserver \n";
        
       #create config file at /tmp

       my @config_file = split ",", $dr_config;

       system("rm -f $configfile_name");
       system("touch $configfile_name");
        foreach my $config (@config_file) {
           if ($config ne "") {
              #print "line: $config\n"; 
             $config =~ s/[\r\n]//;
             system("echo \"$config\" >> $configfile_name");
           } else {
              next;
           }
        }
       my $fileinfo = `cat $configfile_name`;
       #print "file info \n$fileinfo\n\n";


        #import file to firebox
        my $importfile = "0606_drconfig.txt"; 
        my $cmd = "import route-config $type from ftp://automation:password\@$fileserver/$importfile";                
        @result = $self->cmd("$cmd");

       #clean up file
       system("rm -f $configfile_name");

         if($if_new_handle){
            $self->disconnect;
         } else{
            $self->switch_to_cli;
         }

       return @result;
}


# /*!
#    @function enable_dr 
#    @param $type 
#    @return ok|nok 
#    @discussion 
#    <h2>Description:</h2> 
#    <pre>    enable d-route on firebox</pre>
#    <h2>Example:</h2>
#    <pre>    my $mycli = new XtmCli();
#    <pre>    my $type = "ospf";
#    <pre>    my $mycli->enable_dr($type);</pre>
# */
sub enable_dr {
    
        my $self=shift;
        my $type=shift;
        my @result;

        my $cliresult = $self->connect_cli; 
        my $if_new_handle = $self->if_new_handle;
        print "XtmCli: if_new_handle=$if_new_handle\n";
        
        if($cliresult eq 'nok'){
            print "XtmCli:connect cli fail, return nok\n";
            return 'nok';
        } else{
    #ip dynamic-routing enable
    #ip dynamic-routing ospf enable
            @result = $self->cmd("config","ip dynamic-routing enable",
                       "ip dynamic-routing $type enable");
            
        }

         if($if_new_handle){
            $self->disconnect;
         } else{
            $self->switch_to_cli;
         }
         
         return @result;
    
}

sub disable_dr {    
        my $self=shift;
        my $type=shift;
        my @result;

         my $cliresult = $self->connect_cli; 
         my $if_new_handle = $self->if_new_handle;
        print "XtmCli: if_new_handle=$if_new_handle\n";
        
        if($cliresult eq 'nok'){
            print "XtmCli:connect cli fail, return nok\n";
            return 'nok';
         } else{
             if ($type ne "") {
                 @result = $self->cmd("config", "no ip dynamic-routing $type enable");
              } else {
                 @result = $self->cmd("config", "no ip dynamic-routing enable");         
              }
         }

         if($if_new_handle){
            $self->disconnect;
         } else{
            $self->switch_to_cli;
         }
         
         return @result;
}

sub show_ip_dr_config {
         my $self=shift;
         my $result;
         my @infor;
         
         $result = $self->connect_cli; 
         my $if_new_handle = $self->if_new_handle;
         print "XtmCli:if_new_handle=$if_new_handle\n";
         
         if($result eq 'nok'){
             print "XtmCli:connect cli fail, return nok\n";
             return 'nok';
         } else{
             @infor =$self->cmd("show ip dynamic-routing");
         }
         
         if($if_new_handle){
            $self->disconnect;
         } else{
            $self->switch_to_cli;
         }
         
        return @infor;

}

# /*!
#    @function fips_enable
#    @param null.
#    @return result[0] is ok/nok, array result[1] will return the result
#    @discussion 
#    <h2>Description: </h2>
#    <pre>this method return the result of execute the commands.
#    <h2>example:</h2>
#    <pre>  my @result1 = $dut->cli->fips_enable;</pre>
# */
sub fips_enable{
    my $self=shift;
    my @result;
    my $result1;
    my @result2;
    my $result3;
    my $model=$self->obtain_model;

    #try to use cli
    $result1=$self->connect_cli;
    @result2=$self->cmd("fips enable");
    $result3=$self->disconnect;
    if(($result1 eq 'ok')&&($result2[0] eq 'ok')&&($result3 eq 'ok')){
        $result[0] = 'ok';
    }else{
        print "XTM: Connect/disconnect the device or type the command failed.\n";
        $result[0] = 'nok';
        return @result;
    }
    if($result2[1] =~ m/Error:\s+Enable\s+FIPS\s+mode\s+failed,\s+FIPS\s+is\s+already\s+enabled/i){
        print "XTM: The device had been enable the fips...\n";
        $result[0] = 'nok';
        $result[1] = $result2[1];
        return @result;
    }
    #TBD, need sleep corresponding time to make sure box boot up.
    my $cm = new ConfigurationManager;
    my $series;
    if($model =~ m/[XxTtMm]2050/){
        $series = 'xtm20series';
    }elsif($model =~ m/[XxTtMm]2/){
        $series = 'xtm2series';
    }elsif($model =~ m/[XxTtMm]5/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]3/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]8/){
        $series = 'xtm8series';
    }elsif($model =~ m/[XxTtMm]10/){
        $series = 'xtm10series';
    }elsif($model =~ m/[XxTtMmVv]/){
        $series = 'xtmv';
    }

    my $sleep_time = $cm->getRebootWaitTime($series);
    print "XTM:enable fips will reboot, sleep $sleep_time seconds for XTM booting up before return ...\n";
    sleep ($sleep_time);
    my $i;
    my $mgmt_ip=$self->{HostIP};
    $result3=`ping $mgmt_ip -c 5`;
    for($i=0;$i<10;$i++){
        if($result3 !~ m/64\s+bytes\s+from/i){
            print "$result3\n";
            sleep 10;
            $result3=`ping $mgmt_ip -c 5`;
        }else{
            print "$result3\n";
            last;
        }
    }
    if($i==10){
        print "XTM: the device reboot failed, need check by manaul...\n";
        $result[0] = 'nok';
    }
    print "XTM: The device reboot success...\n";
    $result[1]=$result2[1];
    return @result;
}

# /*!
#    @function fips_disable
#    @param null.
#    @return result[0] is ok/nok, array result[1] will return the result
#    @discussion 
#    <h2>Description: </h2>
#    <pre>this method return the result of execute the commands.
#    <h2>example:</h2>
#    <pre>  my @result1 = $dut->cli->fips_disable;</pre>
# */
sub fips_disable{
    my $self=shift;
    my @result;
    my $result1;
    my @result2;
    my $result3;
    my $model=$self->obtain_model;
    
    #try to use cli
    $result1=$self->connect_cli;
    @result2=$self->cmd("no fips enable");
    $result3=$self->disconnect;
    if(($result1 eq 'ok')&&($result2[0] eq 'ok')&&($result3 eq 'ok')){
        $result[0] = 'ok';
    }else{
        print "XTM: Connect/disconnect the device or type the command failed.\n";
        $result[0] = 'nok';
        return @result;
    }
    if($result2[1] =~ m/Error:\s+Enable\s+FIPS\s+mode\s+failed,\s+FIPS\s+is\s+already\s+enabled/i){
        print "XTM: The device had been disable the fips...\n";
        $result[0] = 'nok';
        $result[1] = $result2[1];
        return @result;
    }
    #TBD, need sleep corresponding time to make sure box boot up.
    my $cm = new ConfigurationManager;
    my $series;
    if($model =~ m/[XxTtMm]2050/){
        $series = 'xtm20series';
    }elsif($model =~ m/[XxTtMm]2/){
        $series = 'xtm2series';
    }elsif($model =~ m/[XxTtMm]5/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]3/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]8/){
        $series = 'xtm8series';
    }elsif($model =~ m/[XxTtMm]10/){
        $series = 'xtm10series';
    }elsif($model =~ m/[XxTtMmVv]/){
        $series = 'xtmv';
    }

    my $sleep_time = $cm->getRebootWaitTime($series);
    print "XTM: disable fips will reboot, sleep $sleep_time seconds for XTM booting up before return ...\n";
    sleep ($sleep_time);
    my $i;
    my $mgmt_ip=$self->{HostIP};
    $result3=`ping $mgmt_ip -c 5`;
    for($i=0;$i<10;$i++){
        if($result3 !~ m/64\s+bytes\s+from/i){
            print "$result3\n";
            sleep 10;
            $result3=`ping $mgmt_ip -c 5`;
        }else{
            print "$result3\n";
            last;
        }
    }
    if($i==10){
        print "XTM: the device reboot failed, need check by manaul...\n";
        $result[0] = 'nok';
    }
    print "XTM: The device reboot success...\n";
    $result[1]=$result2[1];
    return @result;
}

# /*!
#    @function fips_selftest
#    @param null.
#    @return result[0] is ok/nok, array result[1] will return the result
#    @discussion 
#    <h2>Description: </h2>
#    <pre>this method return the result of execute the fips selftest.
#    <h2>example:</h2>
#    <pre>  my @result1 = $dut->cli->fips_selftest;</pre>
# */
sub fips_selftest{
    my $self=shift;
    my @result;
    my $result1;
    my @result2;
    my $result3;
    my $model=$self->obtain_model;

    #try to use cli
    $result1=$self->connect_cli;
    @result2=$self->cmd("fips selftest");
    $result3=$self->disconnect;
    if(($result1 eq 'ok')&&($result2[0] eq 'ok')&&($result3 eq 'ok')){
        $result[0] = 'ok';
    }else{
        print "XTM: Connect/disconnect the device or type the command failed.\n";
        $result[0] = 'nok';
        return @result;
    }
    if($result2[1] =~ m/Error:\s+FIPS\s+power\s+up\s+self\s+tests\s+failed,\s+FIPS\s+isn't\s+enabled\s+yet,\s+can't\s+do\s+this\s+operation/i){
        print "XTM: The deivce fips disable, can't do self test.\n";
        $result[0] = 'nok';
        $result[1] = $result2[1];
        return @result;
    }
    #TBD, need sleep corresponding time to make sure box boot up.
    my $cm = new ConfigurationManager;
    my $series;
    if($model =~ m/[XxTtMm]2050/){
        $series = 'xtm20series';
    }elsif($model =~ m/[XxTtMm]2/){
        $series = 'xtm2series';
    }elsif($model =~ m/[XxTtMm]5/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]3/){
        $series = 'xtm5series';
    }elsif($model =~ m/[XxTtMm]8/){
        $series = 'xtm8series';
    }elsif($model =~ m/[XxTtMm]10/){
        $series = 'xtm10series';
    }elsif($model =~ m/[XxTtMmVv]/){
        $series = 'xtmv';
    }

    my $sleep_time = $cm->getRebootWaitTime($series);
    print "XTM: fips self test will reboot, sleep $sleep_time seconds for XTM booting up before return ...\n";
    sleep ($sleep_time);
    my $i;
    my $mgmt_ip=$self->{HostIP};
    $result3=`ping $mgmt_ip -c 5`;
    for($i=0;$i<10;$i++){
        if($result3 !~ m/64\s+bytes\s+from/i){
            print "$result3\n";
            sleep 10;
            $result3=`ping $mgmt_ip -c 5`;
        }else{
            print "$result3\n";
            last;
        }
    }
    if($i==10){
        print "XTM: the device reboot failed, need check by manaul...\n";
        $result[0] = 'nok';
    }
    print "XTM: The device reboot success...\n";
    $result[1]=$result2[1];
    return @result;
}

# /*!
#    @function obtain_fips_status
#    @param null.
#    @return ok/nok and if it 'ok', array will return the fips status.
#    @discussion 
#    <h2>Description: </h2>
#    <pre>this method return the fips status.
#    <h2>example:</h2>
#    <pre>  my @result1 = $dut->cli->obtain_fips_status;</pre>
# */
sub obtain_fips_status{
    $self=shift;
    my $result;
    my $result1;
    my @result2;
    my $result3;    
        
    $result1=$self->connect_cli;
    @result2=$self->cmd("show fips");
    $result3=$self->disconnect;
    if(($result1 eq 'ok')&&($result2[0] eq 'ok')&&($result3 eq 'ok')){
        $result[0] = 'ok';
    }else{
        print "XTM: Connect/disconnect the device or type the command failed.\n";
        $result = 'nok';
        return $result;
    }
    if($result2[1] =~ m/FIPS\s+status\s+:\s+(\w+)/i){
        $result=$1;
        return $result;
    }else{
        print "XTM: Can not match the FIPS status...";
        $result = 'nok';
        return $result;
    }
    
}

# /*!
#    @function set_wireless_ap
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> set firebox as wireless AP device</pre>
#    <h2>Example:</h2>
#    <pre> 
#       $result[0] = $dut_ap->cli->set_wireless_ap(
#                       ap_mode => 'guest',
#                       site_ip => "$guest_ip/24",
#                       ssid => $wireless_set{'ssid_name'},
#                       auth_method => $wireless_set{'authen'},
#                       encryption => $wireless_set{'encryption'},
#                       rts_threshold=>2346;
#                       frag_threshold=>256;
#                       dhcp_start_ip => $dhcp_start_ip,
#                       dhcp_end_ip => $dhcp_end_ip);
#
#    </pre>
# */

sub set_wireless_ap {
    my $self = shift; 
    my %params = @_;
    my $ap_mode = $params{'ap_mode'};
    my @result_cmd;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->cmd("config",
            "no wireless guest enable",
            "no wireless access-point1 enable",
            "no wireless client enable");
    
    if ($ap_mode eq 'guest') {
        $self->cmd(
            "no wireless guest dhcp enable",
            "no wireless guest mac-acl enable");
        if (exists $params{'site_ip'} ){
              @result_cmd = $self->cmd("wireless guest ".$params{'site_ip'});
              if ($result_cmd[0] eq "nok") {
                    print "set AP as guest mode with IP $params{'site_ip'} failed";
                    return "nok";
              }
        }
        if (exists $params{'ssid'} and exists $params{'auth_method'} and exists $params{'encryption'}){
            @result_cmd = $self->cmd("wireless guest wireless ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'} );
             if ($result_cmd[0] eq "nok") {
                    print "set AP as guest wireless ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'}." failed";
                    return "nok";
              }
        }
        if (exists $params{'dhcp_start_ip'} and exists $params{'dhcp_end_ip'}){
            if (exists $params{'dhcp_reserve'}){
                @result_cmd = $self->cmd("wireless guest dhcp ".$params{'dhcp_start_ip'}." ".$params{'dhcp_end_ip'}." dhcp-reservation ".$params{'dhcp_reserve'},
                                      "wireless guest dhcp enable");
            } else {
                @result_cmd = $self->cmd("wireless guest dhcp ".$params{'dhcp_start_ip'}." ".$params{'dhcp_end_ip'}, 
                                      "wireless guest dhcp enable");
            }
             if ($result_cmd[0] eq "nok") {
                    print "set AP as guest dhcp server with ".$params{'dhcp_start_ip'}." ".$params{'dhcp_end_ip'}." failed";
                    return "nok";
              }
        }
        if (exists $params{'frag_shreshold'}){
            @result_cmd = $self->cmd("wireless guest frag-threshold ".$params{'frag_shreshold'});
             if ($result_cmd[0] eq "nok") {
                    print "set AP fragment threshold with value ".$params{'frag_shreshold'}." failed";
                    return "nok";
              }
        }
        if (exists $params{'rts_shreshold'}){
            @result_cmd = $self->cmd("wireless guest rts-threshold ".$params{'rts_shreshold'});
            if ($result_cmd[0] eq "nok") {
                print "set AP RTS threshold with value ".$params{'rts_shreshold'}." failed";
                return "nok";
            }
        }
        if (exists $params{'mac_acl'}){
            my $mac_tmp = $params{'mac_acl'};
            my @mac_list = split(/,/,$mac_tmp);
            my $length = @mac_list;         
            print("add $length mac address into mac-acl\n");
            if ($length != 0) {
                for(my $i = 0;$i <$length; $i++){
                    print("Add mac_acl :$mac_list[$i]\n");
                    $self->cmd("wireless guest mac-acl $mac_list[$i]");
                }
                $self->cmd("wireless guest mac-acl enable");
                @result_cmd = $self->cmd("show wireless guest");
                if ($result_cmd[1] !~ /\[v\]Restrict access by MAC address/) {
                    print "enable wireless mac-acl failed";
                    return "nok";
                }
            }
        }
        if (exists $params{'broadcast'} and $params{'broadcast'} eq 'no'){
            $self->cmd("no wireless guest broadcast enable");
            @result_cmd = $self->cmd("show wireless guest");
            if ($result_cmd[1] =~ /\[v\]Broadcast SSID and respond to SSID queries/) {
                    print "no enable wireless guest failed";
                    return "nok";
            }
            
        } else {
            $self->cmd("wireless guest broadcast enable");
            @result_cmd = $self->cmd("show wireless guest");
            if ($result_cmd[1] !~ /\[v\]Broadcast SSID and respond to SSID queries/) {
                print "enable wireless guest failed";
                return "nok";
            }
        }
        
        $self->cmd("wireless guest enable");
        @result_cmd = $self->cmd("show wireless guest");
        if ($result_cmd[1] !~ /\[v\]Enable Wireless Guest Network/) {
            print "enable wireless guest failed";
            return "nok";
        }
    } elsif ($ap_mode eq 'access-point1') {
        if (exists $params{'interface_name'} ){
              @result_cmd = $self->cmd("wireless access-point1 interface ".$params{'interface_name'});
              if ($result_cmd[0] eq "nok") {
                    print "set AP as access point mode with interface name $params{'site_ip'} failed";
                    return "nok";
              }
        }
        if (exists $params{'ssid'} and exists $params{'auth_method'} and exists $params{'encryption'}){
            @result_cmd = $self->cmd("wireless access-point1 wireless ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'} );
             if ($result_cmd[0] eq "nok") {
                    print "set AP as access-point wireless ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'}." failed";
                    return "nok";
              }
        }
        if (exists $params{'frag_shreshold'}){
            @result_cmd = $self->cmd("wireless access-point1 frag-threshold ".$params{'frag_shreshold'});
             if ($result_cmd[0] eq "nok") {
                    print "set AP fragment threshold with value ".$params{'frag_shreshold'}." failed";
                    return "nok";
              }
        }
        if (exists $params{'rts_shreshold'}){
            @result_cmd = $self->cmd("wireless access-point1 rts-threshold ".$params{'rts_shreshold'});
            if ($result_cmd[0] eq "nok") {
                print "set AP RTS threshold with value ".$params{'rts_shreshold'}." failed";
                return "nok";
            }
        }
        if (exists $params{'mac_acl'}){
            my $mac_tmp = $params{'mac_acl'};
            my @mac_list = split(/,/,$mac_tmp);
            my $length = @mac_list;         
            print("add $length mac address into mac-acl\n");
            if ($length != 0) {
                for(my $i = 0;$i <$length; $i++){
                    print("Add mac_acl :$mac_list[$i]\n");
                    $self->cmd("wireless access-point1 mac-acl $mac_list[$i]");
                }
                $self->cmd("wireless access-point1 mac-acl enable");
                @result_cmd = $self->cmd("show wireless ap 1");
                if ($result_cmd[1] !~ /\[v\]Restrict access by MAC address/) {
                    print "enable wireless mac-acl failed";
                    return "nok";
                }
            }
        }
        if (exists $params{'broadcast'} and $params{'broadcast'} eq 'no'){
            $self->cmd("no wireless access-point1 broadcast enable");
            @result_cmd = $self->cmd("show wireless ap 1");
            if ($result_cmd[1] =~ /\[v\]Broadcast SSID and respond to SSID queries/) {
                    print "no enable wireless guest failed";
                    return "nok";
            }
            
        } else {
            $self->cmd("wireless access-point1 broadcast enable");
            @result_cmd = $self->cmd("show wireless ap 1");
            if ($result_cmd[1] !~ /\[v\]Broadcast SSID and respond to SSID queries/) {
                print "enable wireless guest failed";
                return "nok";
            }
        }       
        $self->cmd("wireless access-point1 enable");
        @result_cmd = $self->cmd("show wireless ap 1");
        if ($result_cmd[1] !~ /Wireless state\s*:\s*Enabled/) {
            print "enable wireless access-point1 failed";
            return "nok";
        }
    } else {
        print("config AP failed\n");
        return "nok";
    }
    if ($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    
    return  "ok"; 
} ## end of set_wireless_ap

# /*!
#    @function set_wireless_client
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> set firebox as wireless client</pre>
#    <h2>Example:</h2>
#    <pre> 
#       $result[0] = $dut_client->cli->set_wireless_ap(
#                       ssid => $wireless_set{'ssid_name'},
#                       auth_method => $wireless_set{'authen'},
#                       encryption => $wireless_set{'encryption'},
#                       rts_threshold=>2346;
#                       frag_threshold=>256;
#                       dhcp_client => 'any');
#
#    </pre>
# */
sub set_wireless_client {
    my $self = shift; 
    my %params = @_;
    my @result_cmd;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    $self->cmd("config",
                  "no wireless guest enable",
                  "no wireless client enable");
    if (exists $params{'ssid'} and exists $params{'auth_method'} and exists $params{'encryption'}){
        @result_cmd = $self->cmd("wireless client wireless ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'} );
        if ($result_cmd[0] eq "nok") {
            print "set wireless client ".$params{'ssid'}." ".$params{'auth_method'}." ".$params{'encryption'}." failed";
            return "nok";
        }
    }
    if (exists $params{'dhcp_client'}){
        @result_cmd = $self->cmd("wireless client dhcp-client any",
                        "wireless client dhcp-client ".$params{'dhcp_client'});
        if ($result_cmd[0] eq "nok") {
            print "set the wireless dhcp client with ".$params{'dhcp_client'}." failed";
            return "nok";
        }
    }
    if (exists $params{'manual'}){
        @result_cmd = $self->cmd("wireless client manual-conf ".$params{'manual'},);
        if ($result_cmd[0] eq "nok") {
            print "set the wireless client as manual-conf with ".$params{'manual'}." failed";
            return "nok";
        }
        $self->cmd("wireless client enable");
        sleep(3);
        $self->cmd("no wireless client enable");
    }
    @result_cmd = $self->cmd("wireless client enable");
    if ($result_cmd[0] eq "nok") {
        print "enable wireless client failed";
        return "nok";
    }
    @result_cmd = $self->cmd("show wireless client");
    if ($result_cmd[1] !~ /\[v\]Enable wireless as an External interface/) {
        print "enable wireless client failed";
        return "nok";
    }
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return  "ok" ; 
} ## end of set_wireless_ap

sub change_timetype {
    my $time = shift;
    my $i = 1;
    my @args = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
    foreach(@args){
        if($time eq $_){
            last;
        }
        $i++;
    }
    return $i;
}


# /*!
#    @function check_wireless_client_connection
#    @param a list of HASH reference [required] Params are a list of hashes. Each hash should specify the value you want create.
#    @return ok/nok
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> set firebox as wireless client</pre>
#    <h2>Example:</h2>
#    <pre> 
#       $result[0] = $dut_client->cli->check_wireless_client_connection(ap_ip_addr);
#    </pre>
# */
sub check_wireless_client_connection {
    my $self = shift; 
    my $guest_ip = shift; 
    my @result_cmd;
    $self->connect_cli;
    my $if_new_handle = $self->if_new_handle;
    @result_cmd = $self->cmd("ping $guest_ip -c 5");
    if ($result_cmd[1] =~ /bytes from $guest_ip\: icmp_seq=\d ttl=\d{1,3} time=[\d\.]{1,5} ms/i){
            return "ok";
    } else {
        print "because bug67787 not fixed, so re setup wireless one more time";
        $self->cmd("config", "no wireless guest enable");
        sleep(5);
        $self->cmd("wireless guest enable", "exit");
        sleep(15);
        if ($result_cmd[1] =~ /bytes from $guest_ip\: icmp_seq=\d ttl=\d{1,3} time=[\d\.]{1,5} ms/i){
            return "ok";
        } else {
            return "nok";
        }
    }
    if($if_new_handle){
        $self->disconnect;
    }else{
        $self->switch_to_cli;
    }
    return  "ok" ; 
} ## end of set_wireless_ap

#    @function obtain_wb_bypass_list
#    @param the device version
#    @return 'ok' or 'nok' and the wb bypass status list
#    @discussion 
#    <h2>Description: </h2> 
#    <pre> obtain the wb bypass status list</pre>
#    <h2>Example:</h2>
#    <pre> 
#               @result = $dut_client->cli->obtain_wb_bypass_list($version);
#    </pre>
# */
sub obtain_wb_bypass_list{
    my $self = shift;
    my $version = shift;
    my @result;
    $self->connect_shell;
    my $if_new_handle = $self->if_new_handle;
    if($version =~ /11.6/i){
        @result = $self->cmd("cfm_client -w2 status webblocker bypass status");
    }else{
        @result = $self->cmd("wgcmd status /webblocker/bypass_list");
    }
    if($if_new_handle){
       $self->disconnect;
    }else{
       $self->switch_to_cli;
    }
    return @result;
}   

sub _control_ip {
	my $self = shift;
	my $hostip = $self->host_ip();	
	$hostip =~ /(\d+\.\d+\.\d+)\.\d+/i;
	my $cmd = "ifconfig |grep $1 |awk \'{print \$2}\' |awk -F : \'{print \$2}\'" ;
	my $ip_addr = `$cmd`;
	$ip_addr =~ s/\n+//;
	return $ip_addr;
	}

1;
__END__
